En este apartado vamos a aprender a cargar un archivo empleando Django, específicamente el avatar pero, los pasos señalados e implementados aquí los puedes emplear y adaptar para la carga de cualquier tipo de archivo como PDFs.
El proceso de carga de archivos no es un proceso difícil, pero sí consta de varios pasos que debemos de tener en cuenta; veremos paso por paso cada uno de estos pasos para que la implementación sea lo más entendible posible para el lector.
Modelo, formulario y otras configuraciones
Para adaptar el modelo de usuarios para que tenga el campo del avatar, pudiéramos modificar el modelo actual, pero, para evitar modificar un modelo de sistema, lo más recomendado sería crear una relación aparte de tipo uno a uno que contenga los campos adicionales que en este ejemplo, es el del avatar:
account\models.py
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='user/avatar')
Los campos de tipo ImageField establecen la ruta en donde se van a guardar las imágenes.
La relación es de tipo uno a uno OneToOneField ya que esta relación es para extender la relación de usuario con nuevos datos que en este ejemplo es solo el usuario.
Al guardar, veremos un error en la consola:
ERRORS:
account.UserProfile.avatar: (fields.E210) Cannot use ImageField because Pillow is not installed.
HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "python -m pip install Pillow".
Y es que es necesario instalar una dependencia de Python que es empleada para la manipulación de las imágenes al momento de definir el campo ImageField:
$ pip install Pillow
Creamos y ejecutamos las migraciones:
$ python manage.py makemigrations
$ python manage.py migrate
Creamos el formulario del usuario:
account\forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('avatar','user')
El manejo del campo de usuario aplicaremos varios campos en los siguientes apartados ya que por defecto es de tipo selección obligatoria comportamiento que no queremos.
Cargar un archivo, implementación mínima
Para evitar un error del formulario en el cual indique el el usuario es requerido, establecemos en el modelo que dicho campo puede ser nulo:
account\models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True)
También podríamos modificar el formulario como mostramos anteriormente; el siguiente punto a tratar, es colocar el campo de tipo oculto:
account\forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
***
widgets = {'user': forms.HiddenInput() }
Para manejar el formulario, la implementación queda similares a los anteriores casos:
account\views.py
@login_required
def profile(request):
form = UserProfileForm()
if request.method == "POST":
form = UserProfileForm(request.POST, request.FILES)
if form.is_valid():
userprofile = form.save(commit=False)
userprofile.user = request.user
userprofile.save()
return render(request, 'profile.html', {'form':form})
Como consideraciones importantes tenemos:
UserProfileForm(request.POST, request.FILES)
Para establecer los archivos enviados por el usuario, que en este ejemplo es solamente uno, también se conserva la petición con datos planos:
request.POST
Ya que en el formulario anterior pueden haber otros campos de tipo textos o similares.
En cuanto al:
commit=False
Se emplea para solamente generar la instancia del modelo y NO guardar en la base de datos, ya que, antes de guardar a la base de dato se debe de establecer el usuario autenticado:
userprofile = form.save(commit=False)
userprofile.user = request.user
userprofile.save()
Definimos el template con el formulario y las clases de Bootstrap y mostrar los errores:
account\templates\profile.html
{% extends "base_user_account.html" %}
{% load widget_tweaks %}
{% block content %}
{% if form.errors %}
<div class="alert alert-warning">
{{ form.errors }}
</div>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mt-3">
{{ form.avatar|add_label_class:"form-label" }}
{{ form.avatar|add_class:"form-control" }}
</div>
{{ form.user }}
<button type="submit" class="mt-3 btn btn-primary">Send</button>
</form>
{% endblock %}
Con el proceso anterior, se pueden cargar archivos en el sistema, veremos que se genera una carpeta con el archivo cargado en:
user\avatar
Que es la ruta definida en el campo de tipo imagen:
models.ImageField(upload_to='user/avatar')
Y el registro correspondiente en la base de datos; el problema que tenemos actualmente es que, al ser una relación de tipo uno a uno, solamente se puede procesar los datos una sola vez cuando no existe el registro del perfil del usuario en la base de datos, pero, esto lo solventamos en el siguiente apartado.
Otra forma de modificar los atributos del formulario, es directamente desde la clase:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('avatar','user')
def __init__(self,*args,**kwargs):
super(UserProfileForm, self).__init__(*args,**kwargs)
self.fields['user'].widget = forms.HiddenInput()
self.fields['user'].required = False
self.fields['avatar'].widget.attrs['class'] = "custom-file-input"
self.fields['avatar'].widget.attrs['id'] = "customFile"
***
- Andrés Cruz
![Andrés Cruz Andrés Cruz](/public/images/perfil.jpg)
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter