domingo, 20 de abril de 2014

Python, Django, pip y virtualenvs en Ubuntu

Manuales
  • Guía de aprendizaje de Python [web]
  • El libro de Django [pdf]
Instalamos entorno Python/Django [gdggranada.com]:
Ubuntu (12.04) ya trae Python (2.7), aun así comprobamos que está todo:

verdor@enlamina ~$ sudo apt-get install python-virtualenv python-pip python-dev sqlite3 python-sqlite build-essential
verdor@enlamina ~$ #sudo apt-get install python-imaging python-pythonmagick python-markdown python-textile python-docutils #parece que esto es opcional
  • pip es una herramienta para instalar y administrar Paquetes Python (¿RubyGem?)
  • virtualenvs o entornos virtuales (de Python) es un espacio completamente independiente de otros entornos virtuales y de los paquetes instalados globalmente en el sistema (¿RVM?) [Tutorial de Python virtualenv]
No realizar los siguientes pasos... Mejor usar pip y virtualenvs
Podemos instalar Django con apt-get de sistema...

verdor@enlamina ~$ sudo apt-get install python-django
...o bajar los repos...
verdor@enlamina ~$ git clone https://github.com/django/django.git
... o usar pip...
verdor@enlamina ~$ pip install Django==1.6.2 # Estable
verdor@enlamina ~$ # pip install https://www.djangoproject.com/download/1.7b1/tarball/ # version beta 1.7

Continuamos...
Preparamos el Entorno:
Actualizamos pip e instalamos virtualenv (se puede hace con sudo)

verdor@enlamina ~$ pip install --upgrade pip
verdor@enlamina ~$ pip install --upgrade virtualenv # --upgrade es opcional

Preparamos el entorno:

verdor@enlamina ~$ cd src
verdor@enlamina ~/src$ mkdir entorno
verdor@enlamina ~/src$ cd entorno
verdor@enlamina ~/src/entorno$ virtualenv env
New python executable in env/bin/python
Installing setuptools, pip...done.

Activamos el entorno:

verdor@enlamina ~/src/entorno$ source env/bin/activate
(env)verdor@enlamina ~/src/entorno$  # ojo con el prompt

Instalamos django 1.5:

(env)verdor@enlamina ~/src/entorno$ pip install django==1.5
Downloading/unpacking django==1.5
  Downloading Django-1.5.tar.gz (8.0MB): 8.0MB downloaded
...
Successfully installed django
Cleaning up...

Creamos un proyecto (luego distinguiremos entre proyecto y aplicación):

verdor@enlamina ~$ django-admin.py startproject mysite
verdor@enlamina ~$ cd mysite
verdor@enlamina/mysite ~$ python manage.py runserver
Validating models...

0 errors found
April 20, 2014 - 06:51:31
Django version 1.5, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Vamos a comprobar que arranca la aplicación en el navegador http://127.0.0.1:8000/, y esto es lo que vemos:

It worked!

Congratulations on your first Django-powered page.

Of course, you haven't actually done any work yet. Here's what to do next:
  • If you plan to use a database, edit the DATABASES setting in mysite/settings.py.
  • Start your first app by running python manage.py startapp [appname].
You're seeing this message because you have DEBUG = True in your Django settings file and you haven't configured any URLs. Get to work!

¿Y donde esta el código generado de mi proyecto?

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

  • miblog/: El Directorio externo que contiene nuestro projecto.
  • miblog/miblog: El directorio interno que sera el nombre que usaremos para importar el paquete.
  • manage.py: Una utilidad de línea de comandos para interactuar con nuestro proyecto(crear las tablas, iniciar el servidor...).
  • miblog/__init__.py: Un archivo vació requerido para que Python trate a este directorio como un paquete.
  • miblog/settings.py: Configuraciones para este proyecto de Django.
  • miblog/urls.py: La “tabla de contenidos” de nuestro proyecto.
  • miblog/wsgi.py: El archivo encargado de ser compatible con el servidor web.
Configuración del proyecto:
Editamos settings.py. Guias: maestrosdelweb.com django-en-cero-coma-i 1ªDjangoApp
Codificacion de caracteres:

#encoding:utf-8

Ruta del proyecto. Con ello creamos una nueva variable de configuración que guarda la ruta del proyecto, de manera que ahora podemos indicar el resto de rutas de forma relativa a la nueva variable. Además añadimos esta ruta al path de python lo que repercutirá en ventajas futuras.

# Identificando la ruta del proyecto
import os
PROJECT_PATH = os.path.dirname(os.path.realpath(__file__))

Configuramos la base de datos. Usamos sqlite3. También podríamos usar por fácilmente postgresql, mysql y oracle

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'mysite.db',                      # Or path to database file if using sqlite3.
        # The following settings are not used with sqlite3:
        'USER': '',
        'PASSWORD': '',
        'HOST': '',                      # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
        'PORT': '',                      # Set to empty string for default.
    }
}

Otras variables:

TIME_ZONE = 'Europe/Madrid'
LANGUAGE_CODE = 'es-es'

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    'django.contrib.admindocs',
    # Esta es la aplicación que estamos haciendo
    'mysite',
)

Aplicaciones instaladas INSTALLED_APPS: Un proyecto en Django necesita de aplicaciones, algunas ya vienen configuradas de manera predeterminada. En nuestro proyecto usaremos la aplicación de administración y su documentación, estas ya vienen construidas, y también nuestra primera aplicación creada líneas arriba, llamada principal. Para habilitar estas aplicaciones debemos buscar la siguiente sección que se encuentra casi al final del archivo settings.py. Django tendra que crear las tablas de estas aplicaciones adames de las que necesite nuestra propia aplicación mysite.
El resto de configuraciones se iran modificando a medida que lo necesitemos.
Creación de la base de datos. (Ojo, si no esta cargado el envairoment no funcionara):

(env)verdor@enlamina ~/src/entorno/mysite$ python -c "import django; print(django.get_version())" # Prueba 
1.5
verdor@enlamina ~/src/entorno/mysite$ source ../env/bin/activate # Si falla lo cargamos así
(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py sync #Creación BD
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'verdor'): 
Email address: 
Password: 
Password (again): 
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)

Configuramos las direcciones/rutas del proyecto. Fichero urls.py. Es solo descomentar algunas lineas:

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'mysite.views.home', name='home'),
    #url(r'^mysite/', include('mysite.foo.urls')),

    # Uncomment the admin/doc line below to enable admin documentation:
    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)

Arrancamos el servidor y ya podemos ir a la zona de administración http://127.0.0.1:8000/admin

(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py runserver

Este es un buen momento usar git y hacer un comit inicial y montar un repositorio en github
? Antes de seguir. Lo que hemos hecho es crear un proyeco. Un proyecto puede estar compuesto de múltiples aplicaciones (ejemplo las que vienen por defecto en INSTALLED_APPS), y una aplicación se puede integrar en múltiples proyectos.
Creamos una aplicación:
Vamos a realizar un blog

verdor@enlamina ~$ ~/src/entorno/mysite$ source ../env/bin/activate #Cargamos el entorno
(env)samu@sub ~/src/entorno/mysite$ python manage.py startapp blog #Creamos al aplicacion

Ahora tenemos los siguientes ficheros:

mysite/
    manage.py
    blog/
        __init__.py
        models.py
        tests.py
        views.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

Comenzamos con el modelo (ejemplo 1, ejemplo 2) (ORM). Editamos models.py:

from django.db import models

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=200)

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey(Category)
    creation_date = models.DateTimeField(auto_now_add=True)

Antes de probar el modelo, hay que incluir la aplicación blog en el proyecto editando mysite/setting.py

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    'django.contrib.admindocs',
    # Esta es el proyecto
    'mysite',
    # Esta es aplicación
    'blog',
)

Una vez hecho esto ejecutamos el siguiente comando y comprobamos que se generan las consultas esperadas:

(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py sqlall blog
BEGIN;
CREATE TABLE "blog_category" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(200) NOT NULL
)
;
CREATE TABLE "blog_post" (
    "id" integer NOT NULL PRIMARY KEY,
    "title" varchar(200) NOT NULL,
    "content" text NOT NULL,
    "category_id" integer NOT NULL REFERENCES "blog_category" ("id"),
    "creation_date" datetime NOT NULL
)
;
CREATE INDEX "blog_post_6f33f001" ON "blog_post" ("category_id");

COMMIT;

Ahora creamos las tablar realmente:

(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py syncdb
Creating tables ...
Creating table blog_category
Creating table blog_post
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)

Toca el momento de crear las vistas. Editamos blog/views.py e importamos los modelos que vayamos a utilizar:

# Create your views here.
from django.shortcuts import render_to_response

from blog.models import Category
from blog.models import Post

Tambien en blog/views.py añadiremos las funciones que gestionan las peticiones que se hagan al servidor. Algo así como los controladores de RoR.

# Create your views here.
from django.shortcuts import render_to_response

from blog.models import Category
from blog.models import Post

# Vista para un post
# Show
# template/post.html
def one_post(request, idpost):
    post = Post.objects.get(id=idpost)
    
    return render_to_response(
        "post.html",
        {
            "post":post,
        },
    )

# Vista de un listado de posts
# Index
#template/home.html
def home(request):
    posts = Post.objects.order_by("-creation_date")
    
    return render_to_response(
        "home.html",
        {
            "posts":posts,
        },
    )

# Vista de un listado de posts filtrado por category
#template/home.html
def posts_by_category(request, idcategory):
    category = Category.objects.get(id=idcategory)
    posts = category.post_set.order_by("-creation_date")
    
    return render_to_response(
        "home.html",
        {
            "posts":posts,
        },
    )

Ahora creamos las plantillas. Lo que en RoR serian las vistas. Se estarán en la carpeta blog/templates/
Creamos blog/templates/base.html. Se usará de base para generar el resto de las plantillas. Las plantillas heredarán de base.html, y a través de etiqueta block base.html mostrara el código de las plantillas que hereden de esta:

#blog/templates/base.html
<!DOCTYPE html>
<html>
    <head>
    <title>{% block title %}{% endblock %}</title>
    </head>
    
    <body>
    {% block content %}{% endblock %}
    </body>
</html>

Creamos post.html

{% extends "base.html" %}

{% block title %}
    {{ post.title }}
{% endblock %}

{% block content %}
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
{% endblock %}

home.html

{% extends "base.html" %}

{% block title %}Mi blog{% endblock %}

{% block content %}
    
    {% for post in posts %}
        <h1>{{ post.title }}</h1>
        <p>{{ post.content }}</p>
        <hr>
    {% endfor %}

{% endblock %}

Tenemos que indicar en al proyecto donde estan las plantillas, editamos TEMPLATE_DIRS de mysite/settinds.py :

TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    "../blog/templates",
)

Nos quedan crear las URLs de la aplicacion (las rutas). Lo primero indicar al proyecto que busque las URLs de la aplicacion blog. Editamos mysite/urls.py e indicamos que incluya las rutas de blog. Quedaria asi:

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'mysite.views.home', name='home'),
    #url(r'^mysite/', include('mysite.foo.urls')),

    # Uncomment the admin/doc line below to enable admin documentation:
    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
    # Rutas de la aplicacion blog
    url(r'^', include('blog.urls')),
)

Ahora crearemos un fichero blog/urls.py de la aplicación. Debe quedar así:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('',
    url(
         r'^post/(?P[0-9]+)/$',
         'blog.views.one_post', 
         name="one_post"),
    url(
         r'^category/(?P[0-9]+)/$',
         'blog.views.posts_by_category',
         name="posts_by_category"),
    url(
         r'^$',
         'blog.views.home',
         name='home'),
)

Por último vamos hacer accesibles nuestro modelos den blog desde el panel de adminición. Para ello vamos a añadir un fichero admin.py en el directorio blog indicándoselo. Debería quedar así:

from django.contrib import admin
from blog.models import Category
from blog.models import Post

admin.site.register(Post)
admin.site.register(Category)

Símplemente registramos los modelos en el panel de administración. Por último, vamos a ejecutar de nuevo el comando para sincronizar la base de datos, ya que Django Admin añadirá alguna tabla más:

(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py syncdb
Creating tables ...
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py runserver

Ya podemos introducir algún post por la zona de admin http://localhost:8000/admin/ y luego verlo http://localhost:8000/post/1/
Consola Django

(env)verdor@enlamina ~/src/entorno/mysite$ python manage.py shell
Python 2.7.3 (default, Feb 27 2014, 19:58:35) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import django
>>> from blog.models import Post, Category
>>> Category.objects.all()
[, ]
>>> Category.objects.all()[0]

>>> Category.objects.all()[0].id
1
>>> Category.objects.all()[0].name
u'ruby'
>>> Category.objects.all()[1].name
u'lorem'
>>> 

Para que la salida de los objetos por la consola sea mas amigable, tendremos que añadir a los modelos los métodos "def __str__(self):" quedando el fichero bog/models.py de esta manera:

from django.db import models

# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=200)

    def __str__(self):
      return self.name


class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey(Category)
    creation_date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
      return '%s %s' % (self.title, self.creation_date)

No hay comentarios:

Publicar un comentario