1

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?

  • You don't write this in the `User` model, but in the model where you target to write the image. The standard `User` model has no `avatar`, but if you use a `Profile`, you should write it there. – Willem Van Onsem Aug 01 '20 at 20:53
  • A form does not specify how to *persist* data, a model does. A form validates user input. – Willem Van Onsem Aug 01 '20 at 20:56

1 Answers1

0

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>
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • if I already have the User model how can I add another user model to store the avatar? –  Aug 01 '20 at 21:21
  • @BernardoOlivera: this is exactly the scope if this answer, since here we create an extra user model `Profile` to store the avatar, and make a *reference* (a `OneToOneField`) from `Profile` to the `User`. – Willem Van Onsem Aug 01 '20 at 21:23
  • Yes okey, now I understand much better –  Aug 01 '20 at 21:24
  • One last question, so If I do that I dont need to make the media_url and that stuff? –  Aug 02 '20 at 13:44
  • @BernardoOlivera: during development you can indeed let Django serve static/media files, see: https://docs.djangoproject.com/en/3.0/howto/static-files/#serving-files-uploaded-by-a-user-during-development But during production, you need to configure nginx/apache/... to serve static/media files. – Willem Van Onsem Aug 02 '20 at 13:46
  • Also, How can I access the profile image in my template? –  Aug 02 '20 at 13:50
  • @BernardoOlivera: if you have a user, you can use `{{ user.profile.avatar.url }}`, to obtain the media url. So if you have a `Post` model for example with a `ForeignKey` to the user, then for a `post` object, you can use `{{ post.user.profile.avatar.url }}`. – Willem Van Onsem Aug 02 '20 at 13:53