Ticker

6/recent/ticker-posts

Creando blog con Django

En el siguiente post vamos a ver como crear un blog totalmente administrable utilizando el Framework Django

Descarga el código fuente desde el repositorio GitHub en el siguiente enlace

 https://github.com/NorbeyCollazos/Blog_con_Django


Lo primero que hacemos es crear el proyecto, para eso abrimos la consola. En mi caso voy a utilizar el editor de código Visual Studio Code, por lo tanto abro la terminal y me ubico en la carpeta donde quiero que se guarde el proyecto (cd nombre_de_la_carpeta)

A continuación ejecutamos el siguiente comando para crear el proyecto

django-admin startproject Blog_con_Django

Ahora nos ubicamos en la carpeta que se ha creado (cd Blog_con_Django) y ejecutamos el siguiente comando para verificar que se ha creado correctamente

python manage.py runserver

Ahora creamos la primer app que la llamaremos mainapp

python manage.py startapp mainapp

ahora debemos agregar la app en el archivo settings.py

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

Ahora dentro de la app mainapp vamos a crear las vistas, urls y templates

En este caso he descargado una plantilla html para adelantar el proceso de diseño de las páginas. Voy a utilizar la siguiente plantilla que se puede descargar en la siguiente dirección: Devblog V1.1

Dentro de la app mainapp creamos un directorio con el nombre (templates), otro con el nombre (static)

Dentro del directorio templates creamos un directorio con el nombre (layouts) otro con el nombre (mainapp) y otro con el nombre (users)

En el directorio static se deben copiar todas las carpetas que contienen los archivos CSS, JS y Imagenes

En el directorio layouts creamos un archivo con el nombre layout.html que contenga la estructura de la pagina web que se va mostrar en todas las paginas

<!DOCTYPE html>
<html lang="en"> 
<head>
    <title>
        {% block title %}
        {% endblock  %}
    </title>
    
    <!-- Meta -->
    {% load static %}
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Blog Template">
    <meta name="author" content="Xiaoying Riley at 3rd Wave Media">    
    <link rel="shortcut icon" href="{% static 'assets/images/favicon.ico' %}"> 
    
    <!-- FontAwesome JS-->
    <script defer src="https://use.fontawesome.com/releases/v5.7.1/js/all.js" integrity="sha384-eVEQC9zshBn0rFj4+TU78eNA19HMNigMviK/PU/FFjLXqa/GKPgX58rvt5Z8PLs7" crossorigin="anonymous"></script>
    
    <!-- Theme CSS -->  
    <link id="theme-style" rel="stylesheet" href="{% static 'assets/css/theme-1.css' %}">

</head> 

<body>
    
    <header class="header text-center">	    
	    <h1 class="blog-name pt-lg-4 mb-0"><a href="index.html">Anthony's Blog</a></h1>
        
	    <nav class="navbar navbar-expand-lg navbar-dark" >
           
			<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-controls="navigation" aria-expanded="false" aria-label="Toggle navigation">
			<span class="navbar-toggler-icon"></span>
			</button>

			<div id="navigation" class="collapse navbar-collapse flex-column" >
				<div class="profile-section pt-3 pt-lg-0">
				    <img class="profile-image mb-3 rounded-circle mx-auto" src="{% static 'assets/images/profile.png' %}" alt="image" >			
					
					<div class="bio mb-3">Hi, my name is Anthony Doe. Briefly introduce yourself here. You can also provide a link to the about page.<br><a href="about.html">Find out more about me</a></div><!--//bio-->
					<ul class="social-list list-inline py-3 mx-auto">
			            <li class="list-inline-item"><a href="#"><i class="fab fa-twitter fa-fw"></i></a></li>
			            <li class="list-inline-item"><a href="#"><i class="fab fa-linkedin-in fa-fw"></i></a></li>
			            <li class="list-inline-item"><a href="#"><i class="fab fa-github-alt fa-fw"></i></a></li>
			            <li class="list-inline-item"><a href="#"><i class="fab fa-stack-overflow fa-fw"></i></a></li>
			            <li class="list-inline-item"><a href="#"><i class="fab fa-codepen fa-fw"></i></a></li>
			        </ul><!--//social-list-->
			        <hr> 
				</div><!--//profile-section-->
				
				<ul class="navbar-nav flex-column text-left">
					<li class="nav-item active">
					    <a class="nav-link" href="index.html"><i class="fas fa-home fa-fw mr-2"></i>Blog Home <span class="sr-only">(current)</span></a>
					</li>
					<li class="nav-item">
					    <a class="nav-link" href="blog-post.html"><i class="fas fa-bookmark fa-fw mr-2"></i>Blog Post</a>
					</li>
					<li class="nav-item">
					    <a class="nav-link" href="about.html"><i class="fas fa-user fa-fw mr-2"></i>About Me</a>
					</li>
                    
				</ul>
				
				<div class="my-2 my-md-3">
				    <a class="btn btn-primary" href="https://themes.3rdwavemedia.com/" target="_blank">Get in Touch</a>
				</div>
			</div>
		</nav>
    </header>
    
    <div class="main-wrapper">

	    {% block titulo_buscador %}
        {% endblock  %}

	    <section class="blog-list px-3 py-5 p-md-5">
		    <div class="container">
			    
                {% block content %}
                {% endblock  %}
				
		    </div>
	    </section>
	    
	    <footer class="footer text-center py-2 theme-bg-dark">
		   
	        <!--/* This template is released under the Creative Commons Attribution 3.0 License. Please keep the attribution link below when using for your own project. Thank you for your support. :) If you'd like to use the template without the attribution, you can buy the commercial license via our website: themes.3rdwavemedia.com */-->
                <small class="copyright">Designed with <i class="fas fa-heart" style="color: #fb866a;"></i> by <a href="http://themes.3rdwavemedia.com" target="_blank">Xiaoying Riley</a> for developers</small>
		   
	    </footer>
    
    </div><!--//main-wrapper-->
    
    
    
    
    <!-- *****CONFIGURE STYLE (REMOVE ON YOUR PRODUCTION SITE)****** -->  
    <div id="config-panel" class="config-panel d-none d-lg-block">
        <div class="panel-inner">
            <a id="config-trigger" class="config-trigger config-panel-hide text-center" href="#"><i class="fas fa-cog fa-spin mx-auto" data-fa-transform="down-6" ></i></a>
            <h5 class="panel-title">Choose Colour</h5>
            <ul id="color-options" class="list-inline mb-0">
                <li class="theme-1 active list-inline-item"><a data-style="{% static 'assets/css/theme-1.css' %}" href="#"></a></li>
                <li class="theme-2  list-inline-item"><a data-style="{% static 'assets/css/theme-2.css' %}" href="#"></a></li>
                <li class="theme-3  list-inline-item"><a data-style="{% static 'assets/css/theme-3.css' %}" href="#"></a></li>
                <li class="theme-4  list-inline-item"><a data-style="{% static 'assets/css/theme-4.css' %}" href="#"></a></li>
                <li class="theme-5  list-inline-item"><a data-style="{% static 'assets/css/theme-5.css' %}" href="#"></a></li>
                <li class="theme-6  list-inline-item"><a data-style="{% static 'assets/css/theme-6.css' %}" href="#"></a></li>
                <li class="theme-7  list-inline-item"><a data-style="{% static 'assets/css/theme-7.css' %}" href="#"></a></li>
                <li class="theme-8  list-inline-item"><a data-style="{% static 'assets/css/theme-8.css' %}" href="#"></a></li>
            </ul>
            <a id="config-close" class="close" href="#"><i class="fa fa-times-circle"></i></a>
        </div><!--//panel-inner-->
    </div><!--//configure-panel-->

    
       
    <!-- Javascript -->          
    <script src="{% static 'assets/plugins/jquery-3.3.1.min.js' %}"></script>
    <script src="{% static 'assets/plugins/popper.min.js' %}"></script> 
    <script src="{% static 'assets/plugins/bootstrap/js/bootstrap.min.js' %}"></script> 

    <!-- Style Switcher (REMOVE ON YOUR PRODUCTION SITE) -->
    <script src="{% static 'assets/js/demo/style-switcher.js' %}"></script>     
    

</body>
</html> 


Ahora pasamos al archivo views.py donde agregamos la vista para la página de inicio

def index(request):
    return render(request, 'mainapp/index.html',{
        'title': 'Inicio'
    })

Ahora pasamos al archivo urls.py donde vamos a agregar la ruta de la página de inicio

from django.contrib import admin
from django.urls import path

import mainapp.views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', mainapp.views.index, name="index")
]

 

Ahora pasamos al archivo models.py para crear el modelo de la tabla Entradas del blog

from django.db import models
from ckeditor.fields import RichTextField
from django.contrib.auth.models import User

# Create your models here.
class Entrada(models.Model):
    title = models.CharField(max_length=150, verbose_name="Título")
    content = RichTextField(verbose_name="Contenido")
    image = models.ImageField(default='null', verbose_name="Imagen", upload_to="articles")
    public = models.BooleanField(verbose_name="¿Publicado?")
    user = models.ForeignKey(User, editable=False, verbose_name="Usuario", on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="Creado el")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="Actualizado el")


    class Meta:
        verbose_name = "Entrada"
        verbose_name_plural = "Entradas"
    

    def __str__(self):
        return self.title 

Ahora se debe crear la migración

python manage.py makemigrations

Luego migramos al código sql

python manage.py sqlmigrate mainapp 0001

Y luego migramos

python manage.py migrate

No nos olvidemos de agregar la app ckeditor en los setting.py debido a que se está usando en el campo de content

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ckeditor',
    'mainapp.apps.MainappConfig'
]

Ahora vamos al archivo admin.py para agregar el modelo al panel de administración y realizar algunas configuraciones

from django.contrib import admin
from .models import Entrada

class EntradaAdmin(admin.ModelAdmin):
    readonly_fields = ('user','created_at', 'updated_at') #para mostrar campos 
    search_fields = ('title', 'content', 'user__username') #para crear fitro de busqueda
    list_display = ('title', 'user', 'public', 'created_at') #para mstrar columnas
    list_filter = ('public', 'user__username') #para filtrar
    
    #este codigo es para guardar el usuario por defecto
    def save_model(self, request, obj, form, change):
        if not obj.user_id:
            obj.user_id = request.user.id

        obj.save()

# Register your models here.

admin.site.register(Entrada, EntradaAdmin)

 

A continuación vamos a crear el superusuario para acceder al panel de administración

python manage.py createsuperuser

Ahora vamos al archivo apps.py para cambiar el nombre de la tabla que aparece en el panel

from django.apps import AppConfig


class MainappConfig(AppConfig):
    name = 'mainapp'
    verbose_name = 'Gestión de entradas'

despues vamos al archivo settings.py para cambiar la forma de agregar la por la siguiente

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ckeditor',
    'mainapp.apps.MainappConfig'
]

A continuación realizamos las configuraciones para poder subir imágenes

  1. creamos una carpeta con el nombre (media) en la raíz del proyecto, dentro de esa carpeta creamos un directorio con el nombre (articles) o según el nombre que le hayamos colocado en la propiedad upload_to en el campo image del modelo

  2. Ahora vamos al archivo settings.py y agregamos lo siguiente

    #Para Media
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    
  3. Ahora vamos al archivo urls.py y agregamos lo siguiente

    from django.conf import settings
    
    #Ruta para imagenes
    if settings.DEBUG:
        from django.conf.urls.static import static
        urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    

     

A continuación vamos realizar la consulta para mostrar la lista de entradas y de una vez integrando el paginator. Pra eso vvamos al archivo views.py y agregamos el siguiente codigo en la vista del index que es donde se va mostrar el listado

from mainapp.models import Entrada
from django.core.paginator import Paginator

# Create your views here.
def index(request):

    entradas = Entrada.objects.all()

    #paginar los articulos
    paginator = Paginator(entradas, 2)

    #obtener el numero de la pagina
    page = request.GET.get('page')
    page_entradas = paginator.get_page(page)

    return render(request, 'mainapp/index.html',{
        'title': 'Inicio',
        'entradas': page_entradas
    })

Ahora vamos al archivo index.html y realizamos el ciclo for para mostrar el listado de entradas

{% for entrada in entradas %}
					<div class="item mb-5">
				    <div class="media">
					{% if entrada.image != 'null' and entrada.image.url|length >= 1 %}
						<img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="{{entrada.image.url}}" alt="image">					    
						{% else %}
						<img class="mr-3 img-fluid post-thumb d-none d-md-flex" src="{% static 'assets/images/blog/blog-post-thumb-7.jpg' %}" alt="image">
					{% endif %}
					    <div class="media-body">
						    <h3 class="title mb-1"><a href="">{{entrada.title}}</a></h3>
						    <div class="meta mb-1"><span class="date">{{entrada.created_at}}</span><span class="time"></span><span class="comment"><a href="#"></a></span></div>
						    <div class="intro">{{entrada.content|safe}}</div>
						    <a class="more-link" href="{% url 'detalle_entrada' %}">Read more &rarr;</a>
					    </div><!--//media-body-->
				    </div><!--//media-->
			    </div><!--//item-->
				{% endfor %}

Ahora vamos a mostrar los botones para la paginación

<nav class="blog-nav nav nav-justified my-5">
	{% if entradas.has_previous %}
		<a class="nav-link-prev nav-item nav-link rounded-left" href="?page={{entradas.previous_page_number}}">Anterior<i class="arrow-prev fas fa-long-arrow-alt-left"></i></a>
	{% endif %}
	{% if entradas.has_next %}
		<a class="nav-link-next nav-item nav-link rounded-right" href="?page={{entradas.next_page_number}}">Siguiente<i class="arrow-next fas fa-long-arrow-alt-right"></i></a>
	{% endif %}

</nav>

Ahora hacemos lo siguiente para mostrar el detalle de la entrada

  1. Creamos un archivo detalle_entrada.html con la siguiente estructura

    {% extends 'layouts/layout.html' %}
    
    {% block title %}{{entrada.title}}{% endblock  %}
    
    {% block content %}
    
    {% load static %}
    
    <header class="blog-post-header">
    	<h2 class="title mb-2">{{entrada.title}}</h2>
    	<div class="meta mb-3"><span class="date">{{entrada.created_at}}</span><span class="time"></span><span class="comment"><a href="#"></a></span></div>
    </header>
    
    <div class="blog-post-body">
    	<figure class="blog-banner">
    		{% if entrada.image != 'null' and entrada.image.url|length >= 1 %}
    		<a href="#"><img class="img-fluid" src="{{entrada.image.url}}" alt="image"></a>
    		{% else %}
    			<a href="#"><img class="img-fluid" src="{% static 'assets/images/blog/no_image.jpg' %}" alt="image"></a>
    		{% endif %}
    	</figure>
    	<p>{{entrada.content|safe}}</p>
    </div>    
    
    {% endblock  %}
    
  2. Creamos la vista en el archivo views.py

    def detalle_entrada(request, entrada_id):
    
        entrada = Entrada.objects.get(id=entrada_id)
    
        return render(request,'mainapp/detalle_entrada.html',{
            'entrada': entrada
        })
    
  3. Creamos la url en el archivo urls.py

    path('detalle-entrada/<int:entrada_id>', mainapp.views.detalle_entrada, name="detalle_entrada"),
    

A continuación vamos a realizar el proceso para el registro de usuarios

En este caso lo primero que hacemos es entrar al panel de administración con el supersusuario que hemos registrado, luego vamos a crear un grupo con los permisos que le queramos dar a los usuarios que se registren

  1. Dentro de la mainapp vamos a crear un archivo forms.py con el que vamos a personalizar el formulario de registro

    from django import forms
    from django.core import validators
    
    from django.contrib.auth.forms import UserCreationForm
    from django.contrib.auth.models import User
    
    class RegisterForm(UserCreationForm):
    
        
        is_staff = forms.BooleanField(required=True)
    
        class Meta:
            model = User
            fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2','is_staff']
    

     

  2. Creamos el archivo register.html en el directorio templates/users

    {% block title %}{{title}}{% endblock  %}
    
    {% block titulo_buscador %}
    
    <section class="cta-section theme-bg-light py-5">
        <div class="container text-center">
            <h2 class="heading">{{title}}</h2>
            <div class="intro">Regístrate para acceder al panel de administración y crear tus entradas</div>
    
        </div><!--//container-->
    </section>
    
    {% endblock  %}
    
    {% block content %}
    
    {{register_form.errors}}
    
    <form class="signup-form  justify-content-center pt-3" method="POST">
    {% csrf_token %}
        <div class="form-group">
            {% for field in register_form %}
                {{field.label}}
                {{field}}
            {% endfor %}
    
        <br><span>Seleccione el <strong>Is staff</strong> para que pueda acceder al panel de administración</span>
        </div>
        <br>
        <input type="submit" class="btn btn-primary" value="Registrarse"></input>
    </form>
        
    {% endblock  %}
    
  3. Agregamos lo siguiente en el archivo views.py

    from django.shortcuts import render, redirect
    from mainapp.models import Entrada
    from django.core.paginator import Paginator
    from mainapp.forms import RegisterForm
    import sqlite3
    
    def registro_usuario(request):
    
        register_form = RegisterForm()
    
        if request.method == 'POST':
            register_form = RegisterForm(request.POST)
    
            if register_form.is_valid():
                objeto = register_form.save()
                
                idr = str(objeto.id)
    
                cursor.execute("INSERT INTO auth_user_groups (user_id, group_id)VALUES ("+idr+", 1);")
                conexion.commit()
    
                return redirect('/admin')
    
        return render(request, 'users/register.html',{
            'title': 'Registro de usuario',
            'register_form': register_form
        })
    
  4. Creamos la ruto en el archivo urls.py

    path('registro/', mainapp.views.registro_usuario, name="registro_usuario"),
    

Y por último si queremos podemos cambiar el idioma del panel de administración a español, para eso vamos al archivo settings.py y cambiamos LANGUAJE_CODE

LANGUAGE_CODE = 'es-es'

 



Publicar un comentario

0 Comentarios