Why does this work?? Class Meta, model = User

Can someone please explain this?? I’m in the process of trying my first post-tutorial project.

I’ve made a model called Profile with 4 attributes (given_name, surname, bio, image). I’ve made a form called ProfileForm which inherits from UserCreationForm and i’ve added the 4 attributes of my model into the form as form attributes.

MY Question is:

Why does it only work like this

class Meta:
   model = User

This is my models.py file

class Profile(models.Model):
    given_name = models.CharField(max_length=255)
    surname = models.CharField(max_length=255)
    bio = models.TextField(blank=True, null=True)
    image = models.ImageField(upload_to='uploads/', blank=True, null=True)

    def __str__(self):
        return self.given_name

    class Meta:
        ordering = ['given_name']

This is my forms.py file

class ProfileForm(UserCreationForm):
    firstName = forms.CharField(max_length=255)
    lastName = forms.CharField(max_length=255)
    bio = forms.CharField(widget=forms.Textarea)
    image = forms.ImageField()

    class Meta:
        model = User
        fields = ['firstName', 'lastName', 'username', 'password1', 'password2', 'bio', 'image']

This is my views.py file

def sign_up_view(request):
    if request.method == "POST": 

        form = ProfileForm(request.POST, request.FILES) 

        if form.is_valid(): 
            user = form.save()
            login(request, user)

            profile = Profile.objects.create(
                given_name=form.cleaned_data['firstName'], 
                surname = form.cleaned_data['lastName'],
                bio = form.cleaned_data['bio'],
                image = form.cleaned_data['image'])

            

            return redirect('home')

    else:
        form = ProfileForm()   

    return render(request, 'core/sign_up.html', {"form": form})

This is my admin page for a profile. Admin Page Created Profile

This is my admin page for a User

enter image description here

Note : I’m able to achieve my desired outcome, but I’m having trouble in understanding how its working. **Also if i wanted to link the Profile model with the User such that, if the User is deleted in admin then the respective profile would also get deleted??

Answer

It only works with model = User, because your fields, like username, etc. are fields of the User model, not of the Profile model.

What you can do is process the view with two forms. We thus make a form for the Profile model, and use the UserCreationForm for the User.

Furthermore the Profile needs to link to the user model, such that it is clear what Profile belongs to what user. We thus add a ForeignKey with:

from django.conf import settings

class Profile(models.Model):
    # ⋮
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        editable=False
    )

    # ⋮

then we can define a Profile form with:

class ProfileForm(forms.ModelForm):

    class Meta:
        model = Profile
        fields = '__all__'

then we can work with two forms, the view looks like:

from django.contrib.auth.forms import UserCreationForm

def sign_up_view(request):
    if request.method == 'POST':
        user_form = UserCreationForm(request.POST, request.FILES)
        profile_form = ProfileForm(request.POST, request.FILES, prefix='profile')

        if user_form.is_valid() and profile_form.is_valid(): 
            user = form.save()
            profile_form.instance.user = user
            profile_form.save()
            login(request, user)
            return redirect('home')

    else:
        user_form = UserCreationForm()
        profile_form = ProfileForm(prefix='profile')
    context = {'user_form': user_form, 'profile_form': profile_form}
    return render(request, 'core/sign_up.html', context)

and in the template render it with:

<form action="{% url 'name-of-the-signup-view' %}" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ user_form }}
    {{ profile_form }}
</form>

we thus use two Django forms in the same HTML form, and prefix the ProfileForm with profile for the input fields.