Django ModelForm ensuring FK integrity without using it in the form

I have a User Profile model with a Model Form:

class Profile(models.Model):
    # Managed fields
    user     = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
    memberId = models.CharField(unique=True, max_length=15, null=False, blank=False, default=GenerateFA)
    bio = models.TextField(null=True, blank=True)
    avatar   = models.ImageField(upload_to="static/MCARS/img/members", null=True, blank=True)
    birthday = models.DateField(null=True, blank=True)
    gender   = models.CharField(max_length=10, choices=constants.GENDER_CHOICES, null=True, blank=True)
    invited  = models.BooleanField(default=False)
    registered = models.BooleanField(default=False)
    height   = models.PositiveSmallIntegerField(null=True, blank=True)
    phone    = models.CharField(max_length=32, null=True, blank=True)
    address  = models.CharField(max_length=255, null=True, blank=True)
    number   = models.CharField(max_length=32, null=True, blank=True)
    city     = models.CharField(max_length=50, null=True, blank=True)
    state = models.CharField(max_length=50, null=True, blank=True)
    zip      = models.CharField(max_length=30, null=True, blank=True)
    facebook = models.URLField(null=True, blank=True)
    Twitter = models.URLField(null=True, blank=True)
    LinkedIn = models.URLField(null=True, blank=True)
    Instagram = models.URLField(null=True, blank=True)
    Snapchat = models.URLField(null=True, blank=True)
    website = models.URLField(null=True, blank=True)

class UserProfileForm(ModelForm):
    class Meta:
        model = Profile
        exclude = ('user','memberId','invited', 'registered')

I don't want user in the form, but remember who the user is, when saving back to the model. How do I ensure that? Without it, the compiler throws a FK integrity error. This is what I have the view:

@login_required()
def Profile(request, memberid=None):
    if memberid:
        user = User.objects.select_related('profile').get(id=memberid)
    else:
        user = User.objects.select_related('profile').get(id=request.user.id)

    errors = None
    if request.method == 'POST':
        print('found me')
        data = request.POST
        form = UserProfileForm(data)
        form.user = user
        if form.is_valid():
            form.save()
        else:
            print('form is invalid')
            errors = form.errors

    context = {
        'pagetitle': 'MCARS - Service Record',
        'user': user,
        'rank': Rank.objects.get(user=user.id),
        'assignments': Assignment.objects.filter(user=user.id),
        'form': UserProfileForm(),
    }
    if errors:
        context['errors'] = errors
    return render(request, template_name='MCARS/pages/ServiceRecord.html', context=context)

Thanks!

We don't need to fetch the user, since we already got the user: request.user, so we can work with:

@login_required
def Profile(request, memberid=None):
    if request.method == 'POST':
        form = UserProfileForm(request.POST, request.FILES)
        form.user = user
        if form.is_valid():
            form.instance.user = request.user
            form.save()
            return redirect('name-of-some-view')
    else:
        form = UserProfileForm()

    context = {
        'pagetitle': 'MCARS - Service Record',
        'user': request.user,
        'rank': Rank.objects.get(user=request.user),
        'assignments': Assignment.objects.filter(user=request.user),
        'form': form,
    }
    return render(
        request, template_name='MCARS/pages/ServiceRecord.html', context=context
    )

You typically don't create a new form when the POST request fails, but you rerender the form, which will, if you render the form correctly, show the errors, together with the filled in data.

You also normally redirect if the form is valid and correct to implement the Post/Redirect/Get architectural pattern [wiki].

Вернуться на верх