Archivos en MEDIA Django 1.11+ privados (requerir autenticación)

A menudo querremos ofrecer archivos privados en nuestra aplicación Django. La solución más sencilla a esta función es hacer una redirección vía regex a una función que lleve el decorador require_login, de la librería de Django.

Dicha redirección deberá ser incluida en el archivo urls.py principal de nuestro proyecto. Un ejemplo de implementación de esta funcionalidad es el siguiente:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

@login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
    print("Serving " + path)
    return serve(request, path, document_root, show_indexes)

urlpatterns += [
    url( r'^%s(?P<path>.*)$'% settings.MEDIA_URL[1:], protected_serve, {'document_root': settings.MEDIA_ROOT}),
]

Modificando la ruta podrás distinguir qué archivos quedan privados y cuáles no. Si tengo tiempo escribiré un par de ejemplos. Gracias por leerme 😉

Fuente: http://blog.majsky.cz/django-protected-media-files/

Modificar plantilla de Administración en Django 1.11 + (1.8+)

El siguiente snippet pretende ilustrar de forma directa y sencilla cómo funciona el sistema de plantillas que Django incorpora desde la versión 1.8, aplicado a la sobre-escritura de la plantilla de administración. Para detalles técnicos está la fabula documentación de Django.

Comencemos creando una carpeta de nombre templates, dentro de esta otra llamada admin, y dentro de admin un archivo al que llamaremos base_site.html. La estructura deberá quedar como sigue:

proyecto
*proyecto
*app1
*app2
*appn
*templates
**admin
***base_site.html

A continuación entra en la carpeta de plantillas de administración de Django en Github: https://github.com/django/django/tree/master/django/contrib/admin/templates/admin

Dentro de este directorio busca el archivo base_site.html. Ábrelo, copia el código que haya en su interior y pégalo en el archivo base_site.html que creaste en el paso anterior.

{% extends "admin/base.html" %}

{% block title %}Título de la página{% endblock %}

{% block branding %}


&amp;amp;amp;lt;h1 id="site-name"&amp;amp;amp;gt;&amp;amp;amp;lt;a href="{% url 'admin:index' %}"&amp;amp;amp;gt;Cabecera de la página&amp;amp;amp;lt;/a&amp;amp;amp;gt;&amp;amp;amp;lt;/h1&amp;amp;amp;gt;


{% endblock %}

{% block nav-global %}{% endblock %}

Te quedará algo así, y aquí podrás modificar lo que tú quieras, pero para que los cambios aun se vean reflejados aun nos queda un paso más.

Entra en el archivo settings.py de tu proyecto y encuentra el bloque de código TEMPLATES. Dentro del diccionario DIRS, introduce la siguiente ruta:

os.path.join(BASE_DIR, ‘templates’),

Deberá quedar algo como:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },

]

Y ahora sí, al recargar tu página de administración podrás ver que los cambios han surtido efecto. A partir de esto, todo es probar a modificar lo que quieras a partir del proyecto Django original en GitHub.

Fecha de creación y edición automáticas en Django

En Django la fecha de creación y modificación no se añade por defecto a nuestros modelos, como sí ocurre en otros frameworks como Ruby On Rails. La forma más elegante de añadir esta funcionalidad a tus modelos es crear una clase base que herede de models.Model y luego hacer que todos tus modelos hereden de la misma.

class BaseModel(models.Model):
    created_date = models.DateTimeField(_('Date created'), auto_now_add=True)
    modified_date = models.DateTimeField(_('Date modified'), auto_now=True)

    class Meta:
        abstract = True

El resto de nuestros modelos donde queramos tener las fechas de creación y actualización automáticas deberán ser del tipo:

class AccountType(BaseModel):
    name = models.CharField(_('Name of this account type ie: Free'), blank=False, max_length=254)
    description = models.TextField(_('Explain the user what he can and can not do with this account'), blank=True, max_length=512)

Enviar archivos multiform | Django Rest Framework

Creé este snippet para un proyecto en el que necesitaba enviar audio a un servidor vía API REST empleando el formato multipart / form data.

views.py

En el archivo de vistas se encuentra la clase AnswerCreate, que no se encarga ni más ni menos que de crear respuestas de un examen y redirigirlas para almacenarlas debidamente en la base de datos. Dicha clase hereda de CreateAPIView, una clase genérica de DRF sobre la cuál puedes leer aquí, aunque resumiento estipula que sólo podrá ser usada para crear (ni actualizar, ni listar, etc). En nuestro queryset tenemos todas las respuestas actualmente almacenadas en la BD, y en la lista de permisos indicamos que sólo usuarios autenticados podrás crear respuestas.

En nuestra lista de parsers encontramos los necesarios para admitir cabeceras que incluyan archivos. Puedes informarte en mayor profundidad sobre los parsers de DRF aquí.

Lo que encontramos a continuación es el método perform_create sobrescrito para adaptarlo a nuestras necesidades. El resto del código, junto con los ejemplos de Postman son bastante legibles.

class AnswerCreate(generics.CreateAPIView): 
    queryset = Answers.objects.all()
    serializer_class = AnswerSerializer
    # Sólo usuarios autenticados
    permission_classes = (permissions.IsAuthenticated,)
    parser_classes = (MultiPartParser,FormParser,)
    
    def perform_create(self, serializer):
        owner = self.request.user

        # audio-answer es la <em>key</em> de la petición http
        if self.request.data.get('audio-answer') is not None:
            audio_answer = self.request.data.get('audio-answer')
            serializer.save(audio_answer = audio_answer)
        else:
            raise Exception("Al audio no pudo ser enviado")

serializers.py

class AnswerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Answers
        fields = ('id', 'student_name',)

models.py

class Answers(models.Model):
    student_name = models.TextField(default="Test student")
    audio_answer = models.FileField(upload_to='uploads')

Dejo adjuntas un par de capturas con una petición de prueba hecha en postman:

Obtener link de un ImageField en Django Rest Framework

ImageField difiere de FileField en cuanto a que el método .link no está disponible. Enviar vía DRF (Django Rest Framework) el link a una imagen no es un proceso “trivial”. Mi implementación se basa en usar Serializer Method Field. El código que sigue creo que es bastante esclarecedor:

serializers.py

from rest_framework import serializers
from .models import User, Avatar
from django.conf import settings

class AvatarSerializer(serializers.ModelSerializer):
    """ Returns all the avatars """
    download_link = serializers.SerializerMethodField()
    class Meta:
        model = Avatar
        fields = ('id', 'user', 'avatar', 'download_link')

    # GET Must be in front
    def get_download_link(self, obj):
        return '%s/%s' % (settings.MEDIA_ROOT , obj.avatar.name)

models.py

class Avatar(models.Model):
    """ Related_name must be set in order to catch related user avatars in the serializer"""
    user = models.ForeignKey(User, related_name='avatars')
    is_active = models.BooleanField(_('Show this avatar'), default=False)
    avatar = models.ImageField(_('Your profile picture'), blank=False, upload_to=set_avatar_directory, help_text=_('Let others see a picture or artwork that identifies you.'))

Aun así, es posible que exista una forma más óptima de llevar a cabo este proceso. Si la conoces no dudes en escribir un comentario o ponerte en contacto conmigo.

Fecha por defecto en modelo Django 1.11

Existen dos formas de incluir una fecha por defecto en Django. La primera, que había estado empleando en versiones del framework anteriores sería la siguiente:

import datetime
date_of_birth = models.DateField(_('Date of Birth'), default=datetime.date.today())

Sin embargo la forma óptima de establecer esta fecha por defecto es empleando el módulo de Django timezone:

from django.utils import timezone
date_of_birth = models.DateField(_('Date of Birth'), default=timezone.now)

El motivo tiene que ver con el sistema de zonas horarias que viene por defecto activado en Django. Si se emplea la primera opción, tu aplicación no operará de forma correcta cuando se trate de fechas en distintas partes del mundo o del año (cambios anuales).

Configuración rápida mysql Django para Ubuntu 16 / 14

Enlazar mysql con Django puede dar más de un quebradero de cabeza si no se sigue un orden secuencial de instalación de paquetes.

En primer lugar, asegúrate de instalar un servidor de mysql en tu servidor o equipo de desarrollo. Instala tambén libmysqlclient-dev, necesario para conectar python con mysql.

sudo apt-get install mysql-server
sudo apt-get install libmysqlclient-dev

Ahora instala mediante pip o tu gestor de paquetes python la librería mysqlclient.

pip install mysqlclient

A continuación entra en tu proyecto Django y abre el archivo settings.py. En la sección DATABASES, introduce el siguiente fragmento de código:

DATABASES = {
'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': os.path.join(BASE_DIR, 'db.cnf'),
        },
    }
}

Por último, crea un archivo con nombre db.cnf en el directorio raíz de tu aplicación Django, y copia el siguiente texto, sustituyendo las variables por tus datos de configuración:

[client]
database = nombre_de_tu_db
user = usuario_mysql
password = contraseña_usuario
default-character-set = utf8

Esto equivaldría a:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'USER': 'usuario_mysql',
        'PASSWORD': 'contraseña_usuario'
        'NAME': 'nombre_de_tu_db',
    },
}

La base de datos que introduzcas debe ser creada de forma manual, y tras iniciar el servidor y ver que todo funciona correctamente recuerda hacer todas las migraciones pertinentes.

Crear un modelo de autenticación personalizado en Django

A modo de apunte dejo los pasos a seguir para crear un modelo de autenticación en Django 1.11. No voy a ahondar en ningún detalle ya que estos se pueden encontrar en:

1: Registra la app en la cuál vayas a especificar tu modelo de usuario dentro de settings.py.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'tu_app'
]

2: En models.py de ‘tu_app’ añade lo siguiente:

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

3: De vuelta en settings.py añade la siguiente línea:

AUTH_USER_MODEL = 'tu_app.User'

(No importa en qué punto del documento la incluyas)

4: Entra en admin.py de ‘tu_app’ y registra la siguiente entrada:

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

admin.site.register(User, UserAdmin)

5: Migraciones.

python manage.py makemigrations
python manage.py migrate

Y listo.

Añadir un entorno virtual (virtualenv) a Pycharm

Si te encuentras trabajando con Pycharm y a la hora de hacer algún import te encuentras con errores como “Unresolved reference ‘django’“, probablemente se te haya pasado incluir en el path de Pycharm la ruta al entorno virtual con el que estés trabajando. También puede ocurrir que realmente se te haya pasado instalar la dependencia, pero esa es otra historia.

Para añadir tu virtualenv simplemente abre tu proyecto con Pycharm, haz clic en File > Settings > Project: nombre_de_tu_proyecto > Project Interpreter > Rueda dentada > Add local > Busca tu virtual env/bin/python > Ok

Y listo, ya no debería dar problemas.

Acceder a servidor local Django desde otro ordenador

En esta breve guía explicaré cómo podemos iniciar un servidor de desarrollo (o producción) Django y permitir el acceso a otras máquinas desde tu misma red o el exterior.

Para ello iniciamos nuestro servidor de desarrollo Django indicándole que escuche en nuestra interfaz de red:

python manage.py runserver 0.0.0.0:8000

Si ahora queremos que otras máquinas de nuestra red local se conecten a nuestro servidor, tan sólo necesitaremos conocer cuál es la ip de la computadora donde esté corriendo el servidor (mediante por ejemplo ifconfig en linux) para luego concatenar el puerto que estemos usando, 8000 en el caso base de tal forma que quedara, por ejemplo 192.168.10.4:8000.

Accediendo a 192.168.10.4:8000 podremos acceder a nuestra web.