Home > Blockchain >  Django: Foreign Key to User -> Form is not validating because field is required
Django: Foreign Key to User -> Form is not validating because field is required

Time:11-30

I'm currently creating a Registration-Page with two parts

  • One part is about the Username and a Passwort.
  • The second part is about choosing the own PC-Configuration

After defining everything, the User can register to get to the Main-Page.

Therefore I got a Model called "PC_Configuration" with a bunch of Foreign-Keys to the different Database-Models of the Processors/Graphicscards etc.:

class PC_Configuration(models.Model):

user = models.ForeignKey(User, related_name='user_id', on_delete=models.DO_NOTHING)
processor = models.ForeignKey(Processors, related_name='processor_id', on_delete=models.DO_NOTHING)
graphicscard = models.ForeignKey(Graphicscard, related_name='graphicscard_id', on_delete=models.DO_NOTHING)
os = models.ForeignKey(OS, related_name='os_id', on_delete=models.DO_NOTHING)
ram = models.ForeignKey(RAM, related_name='ram_id', on_delete=models.DO_NOTHING)
harddrive = models.ForeignKey(Harddrive, related_name='harddrive_id', on_delete=models.DO_NOTHING)

Also, there is one ForeignKey to the User to connect the Configuration to the respective User-ID.

Inside views.py, I've been creating a DropdownForm for all the Dropdown-Fields which the User shall choose on his own:

class DropdownForm(forms.ModelForm):
class Meta:
    model = models.PC_Configuration
    exclude = []

    def __init__(self, *args, **kwargs):
        super(DropdownForm, self).__init__(*args, **kwargs)
        self.fields['processors'].queryset = DropdownForm.objects.all()
        self.fields['processors'].label_from_instance = lambda obj: "%s" % obj.name

        self.fields['graphicscard'].queryset = DropdownForm.objects.all()
        self.fields['graphicscard'].label_from_instance = lambda obj: "%s" % obj.name

        self.fields['os'].queryset = DropdownForm.objects.all()
        self.fields['os'].label_from_instance = lambda obj: "%s" % obj.name

        self.fields['ram'].queryset = DropdownForm.objects.all()
        self.fields['ram'].label_from_instance = lambda obj: "%s" % obj.name

        self.fields['harddrive'].queryset = DropdownForm.objects.all()
        self.fields['harddrive'].label_from_instance = lambda obj: "%s" % obj.name

But regarding the fact, that the User-ID shall be assigned to the Configuration automatically, there's no field for that in here.

It is defined in the register_view(request) - Method:

def register_view(request):
form = DropdownForm()

if request.method == "POST":
    form = DropdownForm(request.POST)
    username = request.POST.get('username')
    password = request.POST.get('password')

    myuser = User.objects.create_user(username, None, password)
    myuser.save()
    auth.login(request, myuser)

    #form.user = request.user

    print(form.errors)
    if form.is_valid():
        instance = form.save(commit=False)
        instance.user = request.user
        instance.save()
        messages.success(request, "Account has been created successfully")

        return redirect(reverse('gamesearch_view'))

    else:
        print('Failed')
        form = DropdownForm()
        render(request, 'register.html', dict(form=form))

return render(request, 'register.html', dict(form=form))

And in here, we got the problem, I guess. While Testing the Registration, the Testaccounts keep creating and login successfully. But the problem is, that there's no PC-Configuration created because the form is not validating.

With

print(form.errors)

I've been trying to figure out why exactly and it said

<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>

So it seems like it's necessary to define the "user"-field before checking, if the form is validating and defining the user inside an instance afterwards.

That's why I was trying to do this:

   form.user = request.user

But it's still not working and I can't figure out, what's exactly the problem since "user" shouldn't be part of the form-validation.

Can you help me out here?

Thank you in Advance!

CodePudding user response:

You'll have a simpler time with something like this...

  • Your related_names were somewhat bogus; they're supposed to be the reverse name from the "viewpoint" of the other model. (Also, you never need to add _id to your fields by hand in Django.) If you elide the related_names, they'll implicitly be pc_configuration_set.
  • on_delete=DO_NOTHING is likely not a good idea. PROTECT is a good default.
  • It's easier to just handle the username and password as fields in the form.
  • You were missing exclude = ["user"], so if your template didn't render a field for user, of course it'd be missing. However, you also don't want the POSTer of the form to submit any old user id.
  • Using a FormView removes most of the boilerplate required to manage forms.
  • We're using transaction.atomic() to make sure the user doesn't get finally saved to the database if saving the PC Configuration fails.
  • We assign the created user to form.instance, which is the new but as-of-yet unsaved PC Configuration.

(Of course, imagine these are in separate files.)

from django import forms
from django.db import models, transaction
from django.views.generic import FormView


class PC_Configuration(models.Model):
    user = models.ForeignKey(User, on_delete=models.PROTECT)
    processor = models.ForeignKey(Processors, on_delete=models.PROTECT)
    graphicscard = models.ForeignKey(Graphicscard, on_delete=models.PROTECT)
    os = models.ForeignKey(OS, on_delete=models.PROTECT)
    ram = models.ForeignKey(RAM, on_delete=models.PROTECT)
    harddrive = models.ForeignKey(Harddrive, on_delete=models.PROTECT)


class RegisterAndConfigurePCForm(forms.ModelForm):
    username = forms.CharField(required=True)
    password = forms.CharField(required=True, widget=forms.PasswordInput())

    class Meta:
        model = PC_Configuration
        exclude = ["user"]  # we'll assign this by hand


class RegisterAndConfigureView(FormView):
    form_class = RegisterAndConfigurePCForm
    template_name = "register.html"

    def form_valid(self, form):
        with transaction.atomic():
            user = User.objects.create_user(form.cleaned_data["username"], None, form.cleaned_data["password"])
            form.instance.user = user  # assign user to the to-be-created PC configuration
            form.save()
        return redirect(reverse("gamesearch_view"))
  • Related