I'm learning Django and making my (technically second) project.
The project features several apps, including player_interface and manager_interface.
Here's the player_interface/models.py:
from django.db import models
from django.contrib.auth.models import User
class PlayerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT,
limit_choices_to={'is_staff': False})
balance = models.PositiveIntegerField(default=0)
def __str__(self):
return f'PlayerProfile user_id={self.user_id} balance={self.balance}'
In the manager_interface app, I want to create users together with corresponding player profiles. So I made a form (in manager_interface/forms.py):
from django import forms
from django.contrib.auth.models import User
from django.db import transaction
from player_interface.models import PlayerProfile
PlayerProfileUserFormset = forms.inlineformset_factory(
User, PlayerProfile, fields=('balance',),
extra=1, max_num=1, can_delete=False)
class PlayerUserForm(forms.ModelForm):
def __init__(self, data=None, instance=None, **kwargs):
super().__init__(data=data, instance=instance, **kwargs)
self.formset = PlayerProfileUserFormset(data=data,
instance=self.instance)
def is_valid(self):
return super().is_valid and self.formset.is_valid()
@transaction.atomic
def save(self):
super().save()
self.formset.save()
return self.instance
class Meta:
model = User
fields = ('username', 'password')
And this is how I use this form in manager_interface/views.py:
<...>
class CUPlayerMixin:
template_name = 'manager_interface/players_form.html'
form_class = PlayerUserForm
success_url = reverse_lazy('players_list')
class CreatePlayerView(CUPlayerMixin, edit_views.CreateView):
pass
class UpdatePlayerView(CUPlayerMixin, edit_views.UpdateView):
queryset = User.objects.filter(is_staff=False)
slug_field = 'username'
slug_url_kwarg = slug_field
And the relevant template bit is:
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<div>{{ form.formset.as_p }}</div>
<button type="submit">Submit</button>
</form>
{% endblock %}
The code above works.
My question is: is this a good, Django way to handle a form for both a model and its association?
In this case, I edit both the User
model with the associated PlayerProfile
. That's why I decided to use the formset: it would automatically populate the subform for the player profile and then save it with the self.formset.save()
part, keeping track of the association and all.
Thank you very much!
formset.save()
and it will automatically create the player profile with the user instance set, etc. If I understand this correctly, if I just make a simple form, I'll have to create the profile instance manually. \$\endgroup\$PlayerUserForm
is, in fact, aModelForm
), but I would still have to create thePlayerProfile
instance manually, would I not? \$\endgroup\${{ form.as_p }}
and then{{ form.formset.as_p }}
. \$\endgroup\$