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
精彩评论