Perfil de Usuario


Receta :smiling_imp:

Un Modelo

Una forma.

Una vista.

Un url.


El Modelo

models.py

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

class PerfilDeUsuario(models.Model):

    apellido = models.CharField(
        blank=True,
        default='',
        max_length=120
    )
    avatar = models.ImageField(
        blank=True,
        on_submit='/avatars/',
        null=True
    )
    bio = models.TextField(
        blank=True
    )
    email = models.EmailField(
        blank=True,
        unique=True
    )
    primer_nombre = models.CharField(
        blank=True,
        default='',
        max_length=120
    )
    usuario = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        unique=True
    )

La Forma

forms.py

from django.forms import ModelForm

from .models import PerfilDeUsuario

class FormaDeUsuario(ModelForm):
    class Meta:
        model = PerfilDeUsuario
        fields = [
            'avatar'
            'primer_nombre',
            'apellido',
            'email',
            'bio'
            ]

    # Override y escribe el contrato de init de nuevo como quieras,
    # puedes anadir atributos a los inputs, validantes de datos, etc.
    def __init__(self, *args, **kwargs):
        super(FormaDeUsuario, self).__init__(*args, **kwargs):
        ...
        ...
        ...

Para un ejemplo de un contrato de forma anulado y reiniciado visita este repositorio.

La Vista

views.py

# Herramientas de protocolo
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render

# La forma y el modelo
from .forms import FormaDeUsuario
from .models import PerfilDeUsuario

# La "vista"
@login_required(login_url='/inciar_sesh/')
def perfil_usuario(request):
    """ Una vista para ver y editar el perfil de el usuario """

    # Aunque la vista esta protejida por medio de el decorador
    # de funciones login_required() aqui se implementa una clausla
    # de contrato si el usario no esta autenticado, clausulas
    # mas personalizadas se pueden implementar, como por ejemplo,
    # revisar que el IP de el usuario (incluido en el request) este
    # blacklisteado por tu administrador de sistemas o por otra lista
    # publica.

    if not request.user.is_authenticated():
        return HttpResponseRedirect(reverse(
            'miapp:perfil_usuario'
            )
        )

    # Prepara la forma para renderearla con informacion inicial, si no,
    # si tienes un post que quieres updatear y solo quieres modificar
    # tres o cuatro cosas no tienes que volver a escribir todo el post.

    usuario_de_sesion = PerfilDeUsuario.objects.get(
                        user=request.user)
    # renderea la forma con valores iniciales
    forma = FormaDeUsuario(
        initial={
            'primer_nombre' : usuario_de_sesion.primer_nombre,
            'apellido' : usuario_de_sesion.apellido,
            'email' : usuario_de_sesion.email,
            'bio' : usuario_de_sesion.bio,
        }
    )

    if request.method == "POST":
        forma = FormaDeUsuario(data=request.POST)

        if forma.is_valid():

            # Por default, la clase de User en Django contiene campos
            # para el primer nombre, el apellido, y el email de un
            # usuario -- aparte de dos campos para la contrasena,
            # que se hashea automaticamente antes de ser guardada
            # a la base de datos, y nombre de usuario.
            # En el modelo se creo una relacion entre el campo 'usuario'
            # y el modelo User, esto simplemente significa que
            # todo Perfil de usuario esta conectado a un User unico y que
            # no pueden existir dos perfiles de usuario con el mismo User.

            # Por eso es necesario updatear los campos de el modelo con 
            # los datos de la forma antes de salvar y despues de confirmar
            # que la forma es valida.

            # En la vista de iniciar secion despues de autenticar a un usuario
            # el usuario es accesible en el http request, esto facilita la
            # logica.

            if forma.cleaned_data['email']:
                User.objects.filter(pk=request.user.pk).\
                update(email=form.cleaned_data['email'])

            if forma.cleaned_data['primer_nombre']:
                User.objects.filter(pk=request.user.pk).\
                update(primer_nombre=form.cleaned_data['primer_nombre'])

            if forma.cleaned_data['apellido']:
                User.objects.filter(pk=request.user.pk).\
                update(apellido=form.cleaned_data['apellido'])

            # finalmente salva la forma y en si los cambios a el modelo de
            # PerfilDeUsuario

            forma.save()

        else:

            # cualquier otra cosa, redirije a el usuario a la misma pagina
            # pero guarda un error en la cabezera para avisarle donde fallo.

            messages.error(request, 'Caracteres invalidos')
            return HttpResponseRedirect(reverse(
                'miapp:perfil_usuario'
                )
            )  

    # Para renderear la forma, es importante usar csrf_token, asi que
    # si tus formas no contienen un token, tu sitio entero es vulnerable a 
    # todo tipo de cross site request forgery attacks, y cualquier persona
    # tiene la oportunidad de darle en la madre a tu sitio.

    # Prepara el contexto y renderea
    avatar = PefilDeUsuario.objects.get(usuario=request.user)
    contexto = {'forma' : forma, 'avatar' : avatar }
    return render(request, 'perfil_usuario.html', contexto)

El Url

urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    ...
    ...
    ...
    url(
        route=r'editar_perfil_usuario/$',
        view=views.perfil_de_usuario,
        name='perfil_usuario'
        )
]