Django - 将更新的信息保存给现有用户

Django - Saving updated information to existing user

我正在创建一个用于注册的多步骤用户表单。它背后的想法是,当用户注册时,它会获取他们的注册信息。然后,它保存用户信息。在注册表单中,如果用户选择了某个选项(rank = 'Anak'),他们将被重定向到获取部落名称的基本表单。我想要它做的是将部落名称保存到刚刚创建的用户帐户中,但我在执行此操作时遇到了麻烦,因为 Django 中的基本表单没有 save() 函数。

forms.py

class RegisterForm(UserCreationForm):
    email = forms.EmailField(
        initial='',
        required=True,
        help_text='Please enter a valid email address'
    )

    rank = forms.ChoiceField(
        label='Are you a PSMC member?',
        choices=SavBlock.models.User.rank,
        initial=False,
        required=True,
        help_text='Member accounts will be validated with your HC.',
    )

    class Meta:
        model = User
        # username = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
        fields = ['username', 'first_name', 'last_name', 'email',
                  'rank', 'password1', 'password2']

    def save(self, commit=True):
        user = super(RegisterForm, self).save(commit=False)
        user.email = self.cleaned_data['email']
        user.ranking = self.cleaned_data['rank']
        if commit:
            user.save()
        return user


class AnakRegisterForm(Form):
    tribe = forms.ChoiceField(
        label='What tribe are you from, Uce?',
        choices=SavBlock.models.Anak.tribe,
        initial=False,
        required=True,
        help_text='Member accounts will be validated with your HC.'
    )

    class Meta:
        model = Anak
        fields = ['tribe']

    def save(self, commit=True):
        user = super(RegisterForm, self).save(commit=False)
        user.tribe = self.cleaned_data['tribe']
        if commit:
            user.save()
        return user

views.py

def register(response):
    context = {}
    # temp_key = {}
    if response.method == "POST":
        form = RegisterForm(response.POST)
        if form.is_valid():
            form.save()
            if form.cleaned_data.get('rank') == 'Anak':
                # temp_key[form.cleaned_data.get('username')] = form.cleaned_data
                return redirect('anak_register')
                # anak_register(form.cleaned_data)

            messages.success(response, 'Registration successful. Please login.')
            return redirect('login')
        else:
            context['register'] = form
    else:
        form = RegisterForm()
        context['register'] = form

    return render(request=response, template_name='register/register.html', context={'form': form})


def anak_register(response):
    context = {}
    if response.method == 'POST':
        anak_form = AnakRegisterForm(response.POST)
        if anak_form.is_valid():
            # TODO: The form obtains the tribe information from the user. Now we must save this information into the
            #  users account.

            """
            anak_form = RegisterForm.save(register)
            return anak_form
            """
        else:
            context['register'] = anak_form

        render(request=response, template_name='register/register.html', context={'form': anak_form})

    else:
        anak_form = AnakRegisterForm(response.POST)
        context['anak_register'] = anak_form
        # messages.error(request, 'Unsuccessful registration. Invalid information.')

    return render(request=response, template_name='register/anak_register.html', context={'form': anak_form})

在重定向到 anak_register 之前,UserCreationForm 会保存用户信息。 anak_form 包含一些我想关联到刚创建的用户帐户的信息,但就像我说的,我不确定该怎么做。可能有更简单的方法

models.py

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return User

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_super', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_staff', True)

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True')
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True')

        return self._create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    rank = [
        ('Supporter', 'Supporter (non-member)'),
        ('Anak', 'Anak'),
        ('Uso', 'Uso'),
        ('Chief', 'Chief'),
    ]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

    username = models.CharField("user name", max_length=50, default='', unique=True)
    email = models.EmailField("email address", max_length=30, unique=True, blank=True)
    first_name = models.CharField("first name", max_length=50)
    last_name = models.CharField("last name", max_length=50)
    is_active = models.BooleanField('active', default=True)
    # password = models.CharField("password", unique=True, max_length=32, default='')
    id = models.AutoField(primary_key=True)
    is_staff = models.BooleanField('staff status', default=False)
    date_joined = models.DateField('date_joined', default=timezone.now)
    ranking = models.CharField(choices=rank, max_length=50, default="Supporter")
    tribe = models.CharField(choices=tribe, max_length=50, default="None")

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email', 'password']  # 'ranking'

    # Magic method returns string of self
    def __str__(self):
        return f"User {self.first_name} {self.last_name} rank {self.rank}".strip()

    @property
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}".strip()


class Anak(User):
    def __init__(self, tribe, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tribe = tribe.title()
        self.rank = User.rank[1]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

我不能 100% 确定是否正确理解了您的意图,但正如我所看到的,您使用自己的 class 重新定义了 Django 的用户以使用其他内容(等级、部落)对其进行扩展。

更优雅的方法(在我看来)是创建一个附加模型,该模型将包含特定字段并与标准 Django 用户模型一对一连接。所以你的 models.py 将是:

from django.db import models
from django.contrib.auth.models import User

class Anak(models.Model):
    rank = [
        ('Supporter', 'Supporter (non-member)'),
        ('Anak', 'Anak'),
        ('Uso', 'Uso'),
        ('Chief', 'Chief'),
    ]

    tribe = [
        ('NaKoaHema', 'Na Koa Hema'),
        ('Alakai', 'Alaka\'i')
    ]

    user = models.OneToOneField(User, on_delete=models.CASCADE) # one Anak instance associated with User instance
    tribe = models.CharField(choices=tribe, max_length=50, default="None")
    ranking = models.CharField(choices=rank, max_length=50, default="Supporter")

    def save(self, *args, **kwargs):
        # Create dependent User if not exist
        if not self.user.pk:
            self.user = User.objects.create_user(username=self.user.username, password=self.user.password)
            self.user.is_staff = False

        # logic that you need before saving (if needed)
        self.tribe = tribe.title()
        self.rank = User.rank[1]

        self.user.save()  # mandatory as create_user is not recognized as save operation

        super().save(*args, **kwargs)

因此,现在您可以使用您的表单创建用户 Anak 与用户一对一关联。

附加提示 - 当用户已经创建并登录时 - 您的 request.user 将包含一个包含相关用户的用户模型对象。所以你可以用它来 create/update 相关的 Anak。即:

if response.method == 'POST':
    anak = models.Anak.objects.get_or_create(user=response.user)  # get Anak if exists, create - otherwise
    anak_form = AnakRegisterForm(instance=anak, data=response.POST)  # update anak from above
    if anak_form.is_valid():
        anak_form.save()

因此,您将使用标准 Django 用户(无需自己重新定义用户)并将 Anak 信息保存在单独的 table 中,仅在需要时作为标准用户行为的扩展创建