开发者

Custom Foreign Key Property

开发者 https://www.devze.com 2023-03-22 16:01 出处:网络
I have a model called Personnel which I\'m using as the profile model for the User model. Here\'s my model

I have a model called Personnel which I'm using as the profile model for the User model. Here's my model

class Personnel(models.Model):
    user = models.OneToOneField(
        User
    )
    phone = models.CharField(
        null=False, max_length=50, verbose_name="Phone"
    )

I need to use the above model in with a ModelForm. Here's my ModelForm:

class PersonnelForm(forms.ModelForm):
    class Meta:
        model = Personnel
        exclude = ('user')

    def __init__(self, *args, **kwargs):
        personnel = Personnel(user=User.objects.create(username=(''.join(choice(ascii_uppercase + digits) for x in range(30)))))
        super(PersonnelForm, self).__init__(
             instance=personnel, *a开发者_JAVA百科rgs, **kwargs
        )

I dont need the user field to be displayed on the web from so I'm excluding it. However I still need to pass a value for it so initiate my PersonnelForm by passing it a Personnel instance. The User mode has a field called email which should be input by the user but I cant display this field as it not a field on my Personnel model.

Can I create a virtual field called email which when set, sets the value of the user.email field and when get, gets the value of the same. Something like so:

@property
def email():
    def fget(self):
        return 'fff'

    def fset(self, email):
        self.user.email = email

I need to it in some manner similar to the above snippet because I''l be using the PersonnelForm with Django's generic CRUD views.

Thanks.

(Please feel free to edit and suggest a better name for the title so it may benefit others as well.)


Using Daniel's advice:

class PersonnelForm(forms.ModelForm):

    email = forms.EmailField(
        required=True, label=_("Email")
    )

    class Meta:
        model = Personnel
        exclude = ('user')

    def save(self, *args, **kwargs):
        commit = kwargs.get('commit', True)
        kwargs['commit'] = False
        personnel = super(PersonnelForm, self).save(*args, **kwargs)
        if not personnel.user: #To prevent creating a new user when updating.
            user = User.objects.create_user(
                email=self.cleaned_data['email'],
                username=(''.join(choice(ascii_uppercase + digits) for x in range(30))),
            )
        if commit:
            personnel.save()
        return personnel

This works well for creating anew record using the ModelForm but I can't seem to update the email or view the email when I'm updating.


There are two misconceptions in your question. Firstly, that you need to pass a value for the field even though you've excluded it; and secondly, that you can't include a field that's not on the model. Neither of these are true.

It's definitely possible to define a separate email field, display it on the form along with the real model fields, and then later use that value to create the User object.

Given that you don't want to modify the view to do this logic (which IMO is the wrong decision, but it's your code), you could do this in an overridden save method:

class PersonnelForm(forms.ModelForm):
    email = forms.CharField()

    class Meta:
        model = Personnel
        exclude = ('user',)

    def save(self, *args, **kwargs):
        # call super method with commit=False, but remember original commit arg
        commit = kwargs.get('commit', True)
        kwargs['commit'] = False
        new_object = super(PersonnelForm, self).save(*args, **kwargs)
        user = User.objects.create_user(
            email=self.cleaned_data['user'],
            username=get_random_username(),
            password=User.objects.make_random_password()
        )
        new_object.user = user
        # now save if commit was not explicitly set to False
        if commit:
            new_object.save()
        return new_object
0

精彩评论

暂无评论...
验证码 换一张
取 消