简体   繁体   中英

Extending Django User model - form population errors

I'm extending Django's (v1.9) built-in User model with Player class, to add some extra properties.

class Player(models.Model):
    TIMEZONES=()
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    ... (player-specific properties here)
    time_zone = models.CharField(max_length=255, choices=PRETTY_TIMEZONE_CHOICES, blank=True, null=True,)

When creating users from Django admin panel, I don't always need to create players, so sometimes only User gets created. As a result, Player and User IDs don't exactly match. Turns out that this leads to a problem when populating ModelForms of models that are linked to Player, like this one:

class City(models.Model):
    player = models.ForeignKey(Player, on_delete=models.CASCADE)
    name = models.CharField(max_length = 100)
    x_coord = models.SmallIntegerField()
    y_coord = models.SmallIntegerField()
    region = models.CharField(max_length = 100)
    def __unicode__(self):
        return str(self.player) + "-" + str(self.name)
    class Meta:
        db_table = 'cities'

class CityForm(ModelForm):
    class Meta:
        model = City
        fields = (
            'name',
            'player',
            'x_coord',
            'y_coord',
            'region')

This modelForm is used when creating a new city. When User ID and Player ID match, there is no problem, player ID gets populated in the form and city is successfully created. When User ID and Player ID are different, player ID is not populated in the form, the form fails to validate, and city creation fails.

I have no problem getting Player ID from request.user, and I could fix up the player ID before validating after getting POST data. I've also added a post-save hook so that Player always gets created, so the IDs will always match. But it seems that form should be populated with player ID in the first place, since user data is accessible and it's a one to one relationship.

What am I missing here?

What you are missing is that when you instantiate a ModelForm to create a new row that's related to some existing object, Django has no way of knowing the id of the related object. You need to tell it somehow.

One way to do that is, when you are displaying the form in response to a GET, use the initial argument to the form constructor:

myform = MyModelFormClass(None, initial={ 'myfkfield': myrelatedobject.pk })

Now the form class knows what value to pre-fill in when it renders the form, and when the form is posted, that field will be posted with it.

The other way to do it would be to omit the relation field from your form altogether, then fill it in later before you save, by using the commit argument to the form save method:

myform = MyModelFormClass(request.POST)
# this causes form values to be filled into the instance without actually 
# writing to the database yet.
myinstance = myform.save(commit=False)
myinstance.myfkfield = myrelatedobject
# now really write to database
myinstance.save()

Note that this would be for an insert. For updates, you need to supply the existing object to your modelform constructor, like this:

myinstance = MyModel.objects.get(pk=self.kwargs.pk)
myform = MyModelFormClass(request.POST, instance=myinstance)

Without the instance, ModelForm doesn't know what row it's updating in the database. You would think that this is all present in the HTML so it shouldn't be necessary.. but that's not how Django works. You need to fetch the object existing from the database, and pass it to the ModelForm constructor along with the request.POST data. Then when you call myform.save() it will validate the form, merge its data with the existing object, and save the object. Using commit=False results in the last of those three steps being deferred, so that you can make any adjustments or checks to the updated instance before it is actually saved.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM