Search code examples
django-modelsdjango-formsimagefield

Django: Use ImageField in Modelforms


So I have this model forms:

User = get_user_model()
class UserRegisterForm(forms.ModelForm):
    username = forms.CharField(label='', widget=forms.TextInput(attrs={'placeholder': 'Username'}))
    email = forms.EmailField(label='', widget=forms.TextInput(attrs={'placeholder': 'Email Address'}))
    email2 = forms.EmailField(label='', widget=forms.TextInput(attrs={'placeholder': 'Confirm Email'}))
    password = forms.CharField(label='', widget=forms.PasswordInput(attrs={'placeholder': 'Password'}))
    avatar = forms.ImageField(upload_to='profile_images')

class Meta:
    model = User 
    fields = [
        'username',
        'email',
        'email2',
        'password'
    ]

But this appear

TypeError: __init__() got an unexpected keyword argument 'upload_to'

The problem is that I think it will works if I add the ImageField object to the model, but I dont have a model, As you can see I am using the get_user_model(), is there a way to use upload_to in model forms, or how can I add to the default 'get_user_model' the ImageField object?


Solution

  • You should make an extra model then to store the avatar, or you can make a custom user model.

    If you make an extra model, for example Profile, you can define this as:

    # app/models.py
    
    from django.conf import settings
    from django.db import models
    
    class Profile(models.Model):
        user = models.OneToOneField(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
            editable=False
        )
        avatar = models.ImageField(upload_to='profile_images/')

    Then you can make two ModelForms for example. In fact for the User, you better use the UserCreationForm [Django-doc], since this already implements logic to validate if the passwords match, and it will also transparently hash the password.

    You thus can define a ModelForm for the Profile, and work with:

    # app/forms.py
    
    from django import forms
    from app.models import Profile
    
    class ProfileForm(forms.ModelForm):
        class Meta:
            model = Profile
            fields = ['avatar']

    In the view, you then work with two forms:

    # app/views.py
    
    from app.forms import ProfileForm
    from django.contrib.auth.forms import UserCreationForm
    
    def some_view(request):
        if request.method == 'POST':
            form = UserCreationForm(request.POST, request.FILES)
            form2 = ProfileForm(request.POST, request.FILES)
            if form.is_valid() and form2.is_valid():
                user = form.save()
                form2.instance.user = user
                form2.save()
                return redirect('name-of-some-other-view')
        else:
            form = UserCreationForm()
            form2 = ProfileForm()
        return render(request, 'name-of-some-template.html', {'form': form, 'form2': form2})

    and you thus render both forms in the same <form> tag:

    <form action="{% url 'some_view' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form }}
        {{ form2 }}
    </form>