Search code examples
pythondjangoformsauthenticationdjango-forms

How to fix Cannot resolve keyword 'username' into field. Choices are: id, profile_image, status, user, user_id


I am a beginner in Django, and I have an academy project. I extended the User model by adding some fields to it and used them in a form to allow users to log in. When I try it, it works, but when I submit the form, I get this error: Cannot resolve keyword 'username' into field. Choices are: id, profile_image, status, user, user_id

I know this question is repetitive, but all the cases I've seen were not similar to mine.

models.py

from django.contrib.auth.models import User
from django.db import models

STATUS_CHOICES = (
    ('', 'What is your use of the academy?'),
    ('student', 'Student'),
    ('teacher', 'Teacher'),
    ('author', 'Author'),
)

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    profile_image = models.ImageField(null=True, default='profile.jpg')
    status = models.CharField(
        max_length=150,
        choices=STATUS_CHOICES,
        default='What is your use of the academy?',
    )

    USERNAME_FIELD = 'user__username'

    def __str__(self):
        return self.user.username

views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .forms import RegisterUserForm

# Create your views here.

def profile(request):
    return render(
        request,
        'common/profile.html'
    )

class RegisterView(CreateView):
    form_class = RegisterUserForm
    success_url = reverse_lazy('login')
    template_name = 'registration/register.html'

urls.py

from django.urls import path, include
from django.contrib.auth.views import LoginView
from .forms import UserLoginForm
from . import views


urlpatterns = [
    path('login/', LoginView.as_view(authentication_form=UserLoginForm), name='login'),
    path('register/', views.RegisterView.as_view(), name='register'),
    path('profile/', views.profile, name='profile'),
    path('', include('django.contrib.auth.urls')),
]

register.html

{% extends 'base.html' %}
{% load i18n %}

{% block content %}  

    <div class="container">
        <div class="row justify-content-md-center">
            <div class="card col-md-6 px-0">
                <div class="card-header text-center">
                    <h3 class="mb-0 py-2">{% trans 'Signup' %}</h3>
                </div> 
                <div class="card-body">
                    <form method="post" enctype="multipart/form-data">
                        {% csrf_token %}
                        {% include 'common/form.html' with form=form %}
                        <div class="d-grid gap-2 text-center">
                            <button type="submit" class="btn btn-primary d-grid gap-2 col-6 mx-auto">
                                {% translate 'Signup' %}
                            </button>
                        </div>
                    </form>
                </div>   
            </div>
        </div>
    </div>

{% endblock content %}

This is my repo on github: https://github.com/HamzaWaleedDV/e_academy

I hope for your assistance.

I tried solutions in this proplems:

How to Fix: Cannot resolve keyword 'user' into field. Choices are: description, end_time, id, start_time, title Django Error

Error: Cannot resolve keyword 'id' into field

Cannot resolve keyword 'username' into field. Choices are: city, id, phone, etc


Solution

  • Step 1
    Remove AUTH_USER_MODEL = 'accounts.Profile' from settings.py. You are extending the User model by creating a OneToOneField with your Profile model, so you are still using the Django User model.


    Step 2
    Remove USERNAME_FIELD = 'user__username' from models.py. Again, this is used for custom user models.


    Step 3
    Remove the class Meta from your models.py:

    # REMOVE THIS
    class Meta(UserCreationForm.Meta):
        model = Profile
        fields = UserCreationForm.Meta.fields + ("profile_image","status")
    

    Step 4
    Adjust your forms.py to add the new fields:

    class RegisterUserForm(UserCreationForm):
        
        ...
    
        password2 = forms.CharField(
            label='Password Confirmation',
            widget=forms.PasswordInput(attrs=attrs)
        )
        
        # ADD THESE
        # I haven't worked out how to make them pretty, or how to include
        # the choices for the status, but I figure you can do that
        profile_image = forms.ImageField()
        status = forms.CharField(max_length=20)
    
        field_order = ['first_name', 'last_name', 'username', 'profile_image', 'email', 'password1', 'password2', 'status'] 
    

    Step 5
    Change your RegisterView to save the form, and then add a Profile since the form you have is just creating a User, now you need to create a Profile object connected to that User:

    class RegisterView(CreateView):
        form_class = RegisterUserForm
        success_url = reverse_lazy('login')
        template_name = 'registration/register.html'
    
        def form_valid(self, form):
            form.save()    # The User is now created
    
            # Create a Profile instance for that User
            Profile.objects.create(user=form.instance, status=self.request.POST.get('status'), profile_image=self.request.FILES['profile_image'])
            super(RegisterView, self).form_valid(form)
            return redirect('login')
    

    Finally
    Change your template to SHOW the image:

    {% extends 'base.html' %}
    {% load static %}
    {% load i18n %}
    {% block content %}
    
        <style>
            .vertical-line {
                width: 1px; /* عرض الخط بالبكسل */
                height: 100%; /* الارتفاع يمتد ليغطي العنصر بالكامل */
                background-color: #80808081; /* لون الخط */
                margin: 0 10px; /* هامش يمين ويسار للخط */
            }
        </style>
      
    
        <div class="container">
            <div class="row">
                <div class="card col-md-12 px-0">
                    <div class="card-header">
                        <h1 class="text-center">
                            {% trans 'Hello ' %}{{ user.username }}{% trans '👋' %}
                        </h1>
                    </div> 
                    <div class="card-body">
                        <div class="row">
                            <div class="col-lg-3 text-center">
                                <img src="{{ profile.profile_image.url }}" height="200px" width="200px" style="border-radius: 50%;" alt="{{ user.username }}">
                                <h6>
                                    <span class="badge bg-info">student</span>
                                </h6>
                                <hr>
                                <h5>{{ user.username }}</h5>
                            </div>
                            <div class="col-lg-3 justify-content-start">
                                <div class="vertical-line"></div>
                            </div>                        
                            <div class="col-lg-3">
                                
                            </div>
                        </div>
                    </div>   
                </div>
            </div>
        </div>
    
    {% endblock content %}
    

    Result
    After registering a new user, hyz:
    enter image description here


    Note
    Take a look at the docs, and be aware that if you do end up wanting to use a custom user model (which, the docs themselves actually recommend), it is easy to do at the start of a project, but tricky if you try to do it in the middle of the project. The same docs tell you how.


    -------- EDIT --------
    To allow the user to update the profile image:

    views.py

    @login_required
    def edit_profile(request):
        if request.method == 'POST':
            form = ProfileForm(request.POST, instance=request.user)
            if form.is_valid():
                form.save()
    
                # user data changed, now change the data in the Profile
                profile = Profile.objects.get(user=request.user)
                profile.profile_image = request.FILES['profile_image']
                profile.save()
    
                return redirect('profile')
    
        else:
            form = ProfileForm(instance=request.user)
            return render(request, 'common/profile.html', {
                'form': form
            })
    

    forms.py

    class ProfileForm(UserChangeForm):
        password = None
    
        profile_image = forms.ImageField()
    
        class Meta():
            model = User
            fields = ['first_name', 'last_name', 'email', 'profile_image']
    
            widgets = {
                'first_name': forms.TextInput(attrs=attrs),
                'last_name': forms.TextInput(attrs=attrs),
                'email': forms.EmailInput(attrs=attrs),
            }
        
        # The form saves the User info, now to save the Profile
        def __init__(self, *args, **kwargs):
            super(ProfileForm, self).__init__(*args, **kwargs)
            self.fields['profile_image'].required = False
    

    models.py

    class Profile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
    
        # Added blank=True
        profile_image = models.ImageField(null=True, blank=True, default='academy_courses/static/profile.jpg')
    
        def __str__(self):
            return self.user.username
    

    Make sure to run

    python manage.py makemigrations
    python manage.py migrate