I have a custom user model in which I want to add a user avatar field:
class User(AbstractBaseUser, PermissionsMixin):
# email
# username
# first_name
# last_name
slug = models.SlugField(max_length=100,blank=True)
avatar = models.ImageField(upload_to='avatars',blank=True,null=True,verbose_name='Photo')
# Con este signal indico que el valor del campo username se
# almacene tambien en el campo slug, para generar un URL amigable
# cuando quiera ir a los datos de un usuario tipo 'preferences/username'
@receiver(post_save, sender=User)
def post_save_user(sender, instance, **kwargs):
slug = slugify(instance.username)
User.objects.filter(pk=instance.pk).update(slug=slug)
@property
def image_url(self):
if self.avatar and hasattr(self.avatar, 'url'):
return self.avatar.url
The method image_url()
previous, has been defined in that way because when a user is created for the first time, it has no associated avatar or image.
Then when I call in the template the field avatar.url
this method examines if field avatar has the attribute url
and returns it to paint it in the template in the following way:
<img src="{{ object.image_url|default_if_none:'#' }}" class="img-thumbnail" alt="User Image">
This I'm doing in a template called layout.html
of which all my templates inherit.
In case of not being able to extract the url
of the avatar, which would occur when a user does not have a photo assigned to be created, then nothing is rendered in the template, or it would have an unknown font in the img tag in its source attribute
In this way I handle a little bit that error that would come out When a user is created, this one has no associated, this error to be more precise:
raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)
ValueError: The 'avatar' attribute has no file associated with it.
[13/Jun/2017 15:10:16] "GET /dashboard/ HTTP/1.1" 500 183409
This came out because before in the template layout.html
called the avatar field in the following way:
<img src="{{ userprofile.user.avatar.url }}" class="img-thumbnail" alt="User Image">
userprofile
is a context that I send to this template where all the user's data is, including the avatar
Somehow I have managed it that way, having the following workflow:
url(r"^preferences/(?P<slug>[\w.\-]+)/$",
views.AccountSettingsUpdateView.as_view(),
name='preferences'
),
My form UserUpdateForm()
is quite basic where I call several fields of the user among them the one that interests me that is avatar
:
class UserUpdateForm(forms.ModelForm):
class Meta:
fields = ("first_name", "last_name", "avatar",)
model = get_user_model()
My view AccountSettingsUpdateView
is as follows:
As it is said, the view that handles this form will receive the data file (my image of the field avatar
in this case) in request.FILES
, As is the example , I have dimensioned in some way how the request handling of the form could be in my view, using the post()
method:
I think my problem may be here with the request.POST, request.FILES
just that I do not get well in this part
class AccountSettingsUpdateView(LoginRequiredMixin, UpdateView):
# Obtiene mi modelo de usuario personalizado
model = get_user_model()
# Obtiene el formulario a utilizar
form_class = UserUpdateForm
success_url = reverse_lazy('dashboard')
context_object_name = 'preferences'
def post(self, request, *args,**kwargs):
form = self.get_form()
if request.method=='POST':
form = UserUpdateForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return super(AccountSettingsUpdateView, self).form_valid(form)
else:
return super(AccountSettingsUpdateView, self).form_invalid(form)
'''
# Tenia mis dudas si sobreescribia el form_valid()
def form_valid(self, form):
if self.request.method == 'POST':
form = UserUpdateForm(self.request.POST, self.request.FILES)
if form.is_valid():
form.save()
return super(AccountSettingsUpdateView, self).form_valid(form)
else:
return super(AccountSettingsUpdateView, self).form_invalid(form)
#return render(self.request, 'user_form.html', {'form':form})
'''
My template user_form.html
where I render the form is as follows:
{% extends 'layout.html' %}
{% load bootstrap3 %}
{% block title_tag %}Accounts | Settings | iHost {{ block.super }}{% endblock %}
{% block body_content %}
<div class="container">
<h1>Account Details</h1>
<form enctype="multipart/form-data" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" value="Save Changes" class="btn btn-default">
</form>
</div>
{% endblock %}
What happens to me is that when I enter the Settings option in my application, which calls the url preferences/(?P<slug>[\w.\-]...
referenced above, there my image is rendered because this url calls my view AccountSettingsUpdateView
which has the form UserUpdateForm () in which the avatar field is rendered to upload an image.
But when I go to another part of my application, the avatar image is not rendered. I do not know if I explained myself well, but I attach this animated image to explain what happens to me.
How can I do so that my avatar image, which I am rendering it in a base form that all the other templates inherit, persists in any part of my application?
What can be happening? No doubt there must be some detail that I am missing, perhaps in my forms.py or in my view. I'm inclined towards this last one