jacek

Initial commit

Showing 69 changed files with 1373 additions and 0 deletions
  1 +DEBUG=1
  2 +SECRET_KEY=change_me
  3 +DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
  1 +dist/
  2 +downloads/
  3 +eggs/
  4 +.eggs/
  5 +lib/
  6 +lib64/
  7 +parts/
  8 +sdist/
  9 +var/
  10 +wheels/
  11 +share/python-wheels/
  12 +*.egg-info/
  13 +.installed.cfg
  14 +*.egg
  15 +MANIFEST
  16 +
  17 +# PyInstaller
  18 +# Usually these files are written by a python script from a template
  19 +# before PyInstaller builds the exe, so as to inject date/other infos into it.
  20 +*.manifest
  21 +*.spec
  22 +
  23 +# Installer logs
  24 +pip-log.txt
  25 +pip-delete-this-directory.txt
  26 +
  27 +# Unit test / coverage reports
  28 +htmlcov/
  29 +.tox/
  30 +.nox/
  31 +.coverage
  32 +.coverage.*
  33 +.cache
  34 +nosetests.xml
  35 +coverage.xml
  36 +*.cover
  37 +*.py,cover
  38 +.hypothesis/
  39 +.pytest_cache/
  40 +cover/
  41 +
  42 +# Translations
  43 +*.mo
  44 +*.pot
  45 +
  46 +# Django stuff:
  47 +*.log
  48 +local_settings.py
  49 +db.sqlite3
  50 +db.sqlite3-journal
  51 +
  52 +# Flask stuff:
  53 +instance/
  54 +.webassets-cache
  55 +
  56 +# Scrapy stuff:
  57 +.scrapy
  58 +
  59 +# Sphinx documentation
  60 +docs/_build/
  61 +
  62 +# PyBuilder
  63 +.pybuilder/
  64 +target/
  65 +
  66 +# Jupyter Notebook
  67 +.ipynb_checkpoints
  68 +
  69 +# IPython
  70 +profile_default/
  71 +ipython_config.py
  72 +
  73 +# pyenv
  74 +# For a library or package, you might want to ignore these files since the code is
  75 +# intended to run in multiple environments; otherwise, check them in:
  76 +# .python-version
  77 +
  78 +# pipenv
  79 +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
  80 +# However, in case of collaboration, if having platform-specific dependencies or dependencies
  81 +# having no cross-platform support, pipenv may install dependencies that don't work, or not
  82 +# install all needed dependencies.
  83 +#Pipfile.lock
  84 +
  85 +# PEP 582; used by e.g. github.com/David-OConnor/pyflow
  86 +__pypackages__/
  87 +
  88 +# Celery stuff
  89 +celerybeat-schedule
  90 +celerybeat.pid
  91 +
  92 +# SageMath parsed files
  93 +*.sage.py
  94 +
  95 +# Environments
  96 +.env
  97 +.venv
  98 +env/
  99 +venv/
  100 +ENV/
  101 +env.bak/
  102 +venv.bak/
  103 +
  104 +# Spyder project settings
  105 +.spyderproject
  106 +.spyproject
  107 +
  108 +# Rope project settings
  109 +.ropeproject
  110 +
  111 +# mkdocs documentation
  112 +/site
  113 +
  114 +# mypy
  115 +.mypy_cache/
  116 +.dmypy.json
  117 +dmypy.json
  118 +
  119 +# Pyre type checker
  120 +.pyre/
  121 +
  122 +# pytype static type analyzer
  123 +.pytype/
  124 +
  125 +# Cython debug symbols
  126 +cython_debug/
  127 +
  128 +# CUSTOM
  129 +*.pyc
  130 +__pycache
  131 +.DS_Store
  132 +
  133 +.idea/
  134 +.vscode/
  1 +# Django On Docker
  2 +
  3 +### Quickstart:
  4 +
  5 +- Local
  6 + * docker-compose.yml
  7 + * http://localhost:8001
  8 + ```sh
  9 + $ docker-compose up -d
  10 + ```
  11 +--------------------------------------------------
  12 +
  13 +### Comandos:
  14 +
  15 +- Comandos:
  16 + ```sh
  17 + # Local (docker-compose.yml)
  18 + $ docker-compose exec web python manage.py createsuperuser
  19 + $ docker-compose exec web python manage.py makemigrations
  20 + $ docker-compose exec web python manage.py migrate
  21 + ```
  22 + Más info
  23 + - https://docs.docker.com/compose/reference/
  24 +
  25 +
  26 +- Testing:
  27 + ```sh
  28 + $ docker-compose exec web python manage.py test
  29 + $ docker-compose exec web python manage.py test apps.welcome
  30 + $ docker-compose exec web python manage.py test apps.welcome.tests
  31 + $ docker-compose exec web python manage.py test apps.welcome.tests.test_views
  32 + $ docker-compose exec web python manage.py test apps.welcome.tests.test_views.WelcomeIndexTest
  33 + $ docker-compose exec web python manage.py test apps.welcome.tests.test_views.WelcomeIndexTest.test_view_url_accessible_by_name
  34 +
  35 + $ docker-compose exec web python manage.py test --verbosity 2
  36 + # Los niveles de detalle permitidos son 0, 1, 2 y 3, siendo el valor predeterminado "1".
  37 + ```
  38 +
  39 + Más info
  40 + - https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Testing
  41 +
  1 +# pull official base image
  2 +FROM python:3.9.1
  3 +
  4 +# set work directory
  5 +WORKDIR /usr/src/app
  6 +
  7 +# set environment variables
  8 +ENV PYTHONDONTWRITEBYTECODE 1
  9 +ENV PYTHONUNBUFFERED 1
  10 +
  11 +# install dependencies
  12 +RUN apt-get update && apt-get install -y \
  13 + gettext \
  14 + netcat \
  15 + && rm -rf /var/lib/apt/lists/*
  16 +
  17 +# install dependencies
  18 +RUN pip install --upgrade pip
  19 +COPY ./requirements.txt .
  20 +RUN pip install -r requirements.txt
  21 +
  22 +# copy entrypoint.sh
  23 +COPY ./entrypoint.sh .
  24 +
  25 +# copy project
  26 +COPY . .
  27 +
  28 +# run entrypoint.sh
  29 +ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class CoreConfig(AppConfig):
  5 + name = 'apps.core'
  1 +<html lang="en">
  2 +<head>
  3 + <meta charset="utf-8">
  4 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  5 + <title>Welcome!</title>
  6 + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
  7 + <link rel="stylesheet" href="https://getbootstrap.com/docs/4.0/examples/dashboard/dashboard.css">
  8 +
  9 + <!-- BEGIN: Page CSS -->
  10 + {% block css %}{% endblock %}
  11 + <!-- END: Page CSS -->
  12 +
  13 +</head>
  14 +<body>
  15 +
  16 + <!-- BEGIN: Navbar -->
  17 + {% include 'core/navbar.html' %}
  18 + <!-- END: Navbar -->
  19 +
  20 + <div class="container-fluid">
  21 + <div class="row">
  22 + <!-- BEGIN: Sidebar -->
  23 + {% include 'core/sidebar.html' %}
  24 + <!-- END: Sidebar -->
  25 + <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
  26 + <!-- BEGIN: Content -->
  27 + {% block content %}{% endblock %}
  28 + <!-- END: Content -->
  29 + </main>
  30 + </div>
  31 + </div>
  32 +
  33 + <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
  34 + <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
  35 + <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
  36 +
  37 + <!-- BEGIN: Page JS -->
  38 + {% block javascript %}{% endblock %}
  39 + <!-- END: Page JS -->
  40 +
  41 +</body>
  42 +</html>
  1 +{% load static i18n %}
  2 +<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0">
  3 + <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="/">{% trans 'Django2React' %}</a>
  4 + <ul class="navbar-nav px-3">
  5 + <li class="nav-item text-nowrap">
  6 + <a class="nav-link" href="/admin">{% trans 'Admin' %}</a>
  7 + </li>
  8 + </ul>
  9 +</nav>
  1 +{% load static i18n %}
  2 +{% load startswith %}
  3 +<nav class="col-md-2 d-none d-md-block bg-light sidebar">
  4 + <div class="sidebar-sticky">
  5 + <ul class="nav flex-column">
  6 + <li class="nav-item">
  7 + {% url 'djangoapp:index' as url_djangoapp %}
  8 + <a class="nav-link {% if request.path|startswith:url_djangoapp %} active {% endif %}"
  9 + href="{% url 'djangoapp:index' %}">
  10 + {% trans 'App django' %}
  11 + </a>
  12 + </li>
  13 + <li class="nav-item">
  14 + {% url 'reactapp:index' as url_reactapp %}
  15 + <a class="nav-link {% if request.path|startswith:url_reactapp %} active {% endif %}"
  16 + href="{% url 'reactapp:index' %}">
  17 + {% trans 'App react' %}
  18 + </a>
  19 + </li>
  20 + </ul>
  21 + </div>
  22 +</nav>
  1 +from django import template
  2 +register = template.Library()
  3 +
  4 +
  5 +@register.filter('startswith')
  6 +def startswith(text, starts):
  7 + if isinstance(text, str):
  8 + return text.startswith(starts)
  9 + return False
  1 +from django.shortcuts import render
  2 +
  3 +# Create your views here.
  1 +from django.contrib import admin
  2 +
  3 +from .models import Book
  4 +
  5 +admin.site.register([Book])
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class DjangoappConfig(AppConfig):
  5 + name = 'apps.djangoapp'
  1 +from django.utils.translation import ugettext_lazy as _
  2 +from django import forms
  3 +
  4 +from .models import Book
  5 +
  6 +
  7 +class BookForm(forms.ModelForm):
  8 +
  9 + title = forms.CharField(
  10 + label=_('Titulo'),
  11 + max_length=250,
  12 + required=False,
  13 + widget=forms.TextInput(
  14 + attrs={'class': 'form-control', 'placeholder': _('Titulo')}
  15 + )
  16 + )
  17 +
  18 + class Meta:
  19 + model = Book
  20 + fields = ('title',)
  21 +
  22 + def clean_title(self):
  23 + """
  24 + Validación de titulo.
  25 + :return:
  26 + """
  27 + title = self.cleaned_data.get('title', None)
  28 +
  29 + if not title:
  30 + raise forms.ValidationError(_('Este campo es obligatorio.'))
  31 + if title[0].islower():
  32 + raise forms.ValidationError(_('El título debe empezar por una mayúscula.'))
  33 + return title
  34 +
  35 + def __init__(self, *args, **kwargs):
  36 + super().__init__(*args, **kwargs)
  1 +# Generated by Django 3.1.6 on 2022-03-18 10:37
  2 +
  3 +from django.db import migrations, models
  4 +
  5 +
  6 +class Migration(migrations.Migration):
  7 +
  8 + initial = True
  9 +
  10 + dependencies = [
  11 + ]
  12 +
  13 + operations = [
  14 + migrations.CreateModel(
  15 + name='Book',
  16 + fields=[
  17 + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  18 + ('created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Created')),
  19 + ('modified', models.DateTimeField(auto_now=True, null=True, verbose_name='Modified')),
  20 + ('title', models.CharField(blank=True, max_length=250, null=True)),
  21 + ],
  22 + options={
  23 + 'verbose_name': 'Book',
  24 + 'verbose_name_plural': 'Books',
  25 + },
  26 + ),
  27 + ]
  1 +from django.utils.translation import ugettext_lazy as _
  2 +from django.db import models
  3 +
  4 +
  5 +class Book(models.Model):
  6 +
  7 + created = models.DateTimeField(verbose_name=_('Created'), null=True, blank=True, auto_now_add=True)
  8 + modified = models.DateTimeField(verbose_name=_('Modified'), null=True, blank=True, auto_now=True)
  9 +
  10 + title = models.CharField(max_length=250, null=True, blank=True)
  11 +
  12 + class Meta:
  13 + verbose_name = _('Book')
  14 + verbose_name_plural = _('Books')
  15 +
  16 + def __str__(self):
  17 + return self.title
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'DjangoApp!' %}</h1>
  10 +</div>
  11 +<div class="col">
  12 + <form method="POST">
  13 + {% csrf_token %}
  14 + <!-- title -->
  15 + <div class="form-group {% if form.title.errors %} has-error {% endif %}">
  16 + {{ form.title.label_tag }}
  17 + {{ form.title }}
  18 + {% if form.title.help_text %}
  19 + <span class="help-block">{{ form.title.help_text }}</span>
  20 + {% endif %}
  21 + {% for error in form.title.errors %}
  22 + <span class="help-block">{{ error }}</span>
  23 + {% endfor %}
  24 + </div>
  25 + <div class="d-flex justify-content-between">
  26 + <a href="{%url 'djangoapp:index' %}" class="btn btn-outline-danger">{% trans 'Volver' %} </a>
  27 + <button type="submit" class="btn btn-primary">{% trans "Crear" %}</button>
  28 + </div>
  29 + </form>
  30 +</div>
  31 +{% endblock %}
  32 +
  33 +{% block javascript %}
  34 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'DjangoApp!' %}</h1>
  10 + <div class="btn-toolbar mb-2 mb-md-0">
  11 + <a href="{% url 'djangoapp:create' %}" class="btn btn-sm btn-outline-secondary">
  12 + {% trans 'Crear' %}
  13 + </a>
  14 + </div>
  15 +</div>
  16 +<div class="col">
  17 + <table class="table table-striped table-hover">
  18 + <thead>
  19 + <tr>
  20 + <th>{% trans "ID" %}</th>
  21 + <th>{% trans "Titulo" %}</th>
  22 + <th></th>
  23 + </tr>
  24 + </thead>
  25 + <tbody>
  26 + {% for book in books %}
  27 + <tr>
  28 + <td>{{ book.id }}</td>
  29 + <td>{{ book.title }}</td>
  30 + <td class="text-right text-nowrap">
  31 + <a href="{% url 'djangoapp:read' book.id %}"
  32 + class="btn btn-outline-info">
  33 + {% trans 'Ver' %}
  34 + </a>
  35 + <a href="{% url 'djangoapp:update' book.id %}"
  36 + class="btn btn-outline-primary">
  37 + {% trans 'Editar' %}
  38 + </a>
  39 + <a href="{% url 'djangoapp:delete' book.id %}"
  40 + class="btn btn-outline-secondary">
  41 + {% trans 'Borrar' %}
  42 + </a>
  43 + </td>
  44 + </tr>
  45 + {% endfor %}
  46 + </tbody>
  47 + </table>
  48 +</div>
  49 +{% endblock %}
  50 +
  51 +{% block javascript %}
  52 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'DjangoApp!' %}</h1>
  10 + <div class="btn-toolbar mb-2 mb-md-0">
  11 + <div class="btn-group mr-2">
  12 + <a href="{% url 'djangoapp:update' book.id %}" class="btn btn-sm btn-outline-secondary">{% trans 'Editar' %}</a>
  13 + <a href="{% url 'djangoapp:delete' book.id %}" class="btn btn-sm btn-outline-secondary">{% trans 'Borrar' %}</a>
  14 + </div>
  15 + <a href="{% url 'djangoapp:index' %}" class="btn btn-sm btn-outline-secondary">
  16 + {% trans 'Listar' %}
  17 + </a>
  18 + </div>
  19 +</div>
  20 +<div class="col">
  21 + <p>#{{ book.id }} {{ book.title }}</p>
  22 +
  23 +</div>
  24 +{% endblock %}
  25 +
  26 +{% block javascript %}
  27 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'DjangoApp!' %}</h1>
  10 +</div>
  11 +<div class="col">
  12 + <form method="POST">
  13 + {% csrf_token %}
  14 + <!-- title -->
  15 + <div class="form-group {% if form.title.errors %} has-error {% endif %}">
  16 + {{ form.title.label_tag }}
  17 + {{ form.title }}
  18 + {% if form.title.help_text %}
  19 + <span class="help-block">{{ form.title.help_text }}</span>
  20 + {% endif %}
  21 + {% for error in form.title.errors %}
  22 + <span class="help-block">{{ error }}</span>
  23 + {% endfor %}
  24 + </div>
  25 + <div class="d-flex justify-content-between">
  26 + <a href="{%url 'djangoapp:index' %}" class="btn btn-outline-danger">{% trans 'Volver' %} </a>
  27 + <button type="submit" class="btn btn-primary">{% trans "Actualizar" %}</button>
  28 + </div>
  29 + </form>
  30 +</div>
  31 +{% endblock %}
  32 +
  33 +{% block javascript %}
  34 +{% endblock %}
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
  1 +from django.urls import path, include
  2 +
  3 +from . import views
  4 +
  5 +app_name = 'djangoapp'
  6 +urlpatterns = [
  7 +
  8 + path('djangoapp/', include(([
  9 + path('', views.index, name='index'),
  10 + path('create', views.create, name='create'),
  11 + path('read/<book_id>', views.read, name='read'),
  12 + path('update/<book_id>', views.update, name='update'),
  13 + path('delete/<book_id>', views.delete, name='delete'),
  14 + ])))
  15 +
  16 +
  17 +]
  1 +from django.shortcuts import render, redirect
  2 +
  3 +from .forms import BookForm
  4 +from .models import Book
  5 +
  6 +
  7 +def index(request):
  8 + """
  9 + -- INDEX --
  10 + :param request:
  11 + :return:
  12 + """
  13 + books = Book.objects.all()
  14 +
  15 + return render(request, 'djangoapp/index.html', {
  16 + 'books': books
  17 + })
  18 +
  19 +
  20 +def create(request):
  21 + """
  22 + -- CREATE --
  23 + :param request:
  24 + :return:
  25 + """
  26 +
  27 + form = BookForm()
  28 +
  29 + if request.method == 'POST':
  30 + form = BookForm(request.POST)
  31 + if form.is_valid():
  32 + instance = form.save(commit=False)
  33 + instance.save()
  34 +
  35 + return redirect('djangoapp:index')
  36 +
  37 + return render(request, 'djangoapp/create.html', {
  38 + 'form': form
  39 + })
  40 +
  41 +
  42 +def read(request, book_id):
  43 + """
  44 + -- READ --
  45 + :param request:
  46 + :param id:
  47 + :return:
  48 + """
  49 + book = Book.objects.get(pk=book_id)
  50 +
  51 + return render(request, 'djangoapp/read.html', {
  52 + 'book': book
  53 + })
  54 +
  55 +
  56 +def update(request, book_id):
  57 + """
  58 + -- CREATE --
  59 + :param request:
  60 + :return:
  61 + """
  62 +
  63 + book = Book.objects.get(pk=book_id)
  64 +
  65 + form = BookForm(instance=book)
  66 +
  67 + if request.method == 'POST':
  68 + form = BookForm(request.POST, instance=book)
  69 + if form.is_valid():
  70 + instance = form.save(commit=False)
  71 + instance.save()
  72 +
  73 + return redirect('djangoapp:index')
  74 +
  75 + return render(request, 'djangoapp/update.html', {
  76 + 'form': form
  77 + })
  78 +
  79 +
  80 +def delete(request, book_id):
  81 + """
  82 + -- DELETE --
  83 + :param request:
  84 + :param book_id:
  85 + :return:
  86 + """
  87 +
  88 + Book.objects.get(pk=book_id).delete()
  89 +
  90 + return redirect('djangoapp:index')
  1 +from django.contrib import admin
  2 +
  3 +from .models import Book
  4 +
  5 +admin.site.register([Book])
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class ReactappConfig(AppConfig):
  5 + name = 'apps.reactapp'
  1 +from django.utils.translation import ugettext_lazy as _
  2 +from django import forms
  3 +
  4 +from .models import Book
  5 +
  6 +
  7 +class BookForm(forms.ModelForm):
  8 +
  9 + title = forms.CharField(
  10 + label=_('Titulo'),
  11 + max_length=250,
  12 + required=False,
  13 + widget=forms.TextInput(
  14 + attrs={'class': 'form-control', 'placeholder': _('Titulo')}
  15 + )
  16 + )
  17 +
  18 + class Meta:
  19 + model = Book
  20 + fields = ('title',)
  21 +
  22 + def clean_title(self):
  23 + """
  24 + Validación de titulo.
  25 + :return:
  26 + """
  27 + title = self.cleaned_data.get('title', None)
  28 +
  29 + if not title:
  30 + raise forms.ValidationError(_('Este campo es obligatorio.'))
  31 + if title[0].islower():
  32 + raise forms.ValidationError(_('El título debe empezar por una mayúscula.'))
  33 + return title
  34 +
  35 + def __init__(self, *args, **kwargs):
  36 + super().__init__(*args, **kwargs)
  1 +# Generated by Django 3.1.6 on 2022-03-18 10:37
  2 +
  3 +from django.db import migrations, models
  4 +
  5 +
  6 +class Migration(migrations.Migration):
  7 +
  8 + initial = True
  9 +
  10 + dependencies = [
  11 + ]
  12 +
  13 + operations = [
  14 + migrations.CreateModel(
  15 + name='Book',
  16 + fields=[
  17 + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
  18 + ('created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Created')),
  19 + ('modified', models.DateTimeField(auto_now=True, null=True, verbose_name='Modified')),
  20 + ('title', models.CharField(blank=True, max_length=250, null=True)),
  21 + ],
  22 + options={
  23 + 'verbose_name': 'Book',
  24 + 'verbose_name_plural': 'Books',
  25 + },
  26 + ),
  27 + ]
  1 +from django.utils.translation import ugettext_lazy as _
  2 +from django.db import models
  3 +
  4 +
  5 +class Book(models.Model):
  6 +
  7 + created = models.DateTimeField(verbose_name=_('Created'), null=True, blank=True, auto_now_add=True)
  8 + modified = models.DateTimeField(verbose_name=_('Modified'), null=True, blank=True, auto_now=True)
  9 +
  10 + title = models.CharField(max_length=250, null=True, blank=True)
  11 +
  12 + class Meta:
  13 + verbose_name = _('Book')
  14 + verbose_name_plural = _('Books')
  15 +
  16 + def __str__(self):
  17 + return self.title
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'ReactApp!' %}</h1>
  10 +</div>
  11 +<div class="col">
  12 + <form method="POST">
  13 + {% csrf_token %}
  14 + <!-- title -->
  15 + <div class="form-group {% if form.title.errors %} has-error {% endif %}">
  16 + {{ form.title.label_tag }}
  17 + {{ form.title }}
  18 + {% if form.title.help_text %}
  19 + <span class="help-block">{{ form.title.help_text }}</span>
  20 + {% endif %}
  21 + {% for error in form.title.errors %}
  22 + <span class="help-block">{{ error }}</span>
  23 + {% endfor %}
  24 + </div>
  25 + <div class="d-flex justify-content-between">
  26 + <a href="{%url 'reactapp:index' %}" class="btn btn-outline-danger">{% trans 'Volver' %} </a>
  27 + <button type="submit" class="btn btn-primary">{% trans "Crear" %}</button>
  28 + </div>
  29 + </form>
  30 +</div>
  31 +{% endblock %}
  32 +
  33 +{% block javascript %}
  34 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'ReactApp!' %}</h1>
  10 + <div class="btn-toolbar mb-2 mb-md-0">
  11 + <a href="{% url 'reactapp:create' %}" class="btn btn-sm btn-outline-secondary">
  12 + {% trans 'Crear' %}
  13 + </a>
  14 + </div>
  15 +</div>
  16 +<div class="col">
  17 + <table class="table table-striped table-hover">
  18 + <thead>
  19 + <tr>
  20 + <th>{% trans "ID" %}</th>
  21 + <th>{% trans "Titulo" %}</th>
  22 + <th></th>
  23 + </tr>
  24 + </thead>
  25 + <tbody>
  26 + {% for book in books %}
  27 + <tr>
  28 + <td>{{ book.id }}</td>
  29 + <td>{{ book.title }}</td>
  30 + <td class="text-right text-nowrap">
  31 + <a href="{% url 'reactapp:read' book.id %}"
  32 + class="btn btn-outline-info">
  33 + {% trans 'Ver' %}
  34 + </a>
  35 + <a href="{% url 'reactapp:update' book.id %}"
  36 + class="btn btn-outline-primary">
  37 + {% trans 'Editar' %}
  38 + </a>
  39 + <a href="{% url 'reactapp:delete' book.id %}"
  40 + class="btn btn-outline-secondary">
  41 + {% trans 'Borrar' %}
  42 + </a>
  43 + </td>
  44 + </tr>
  45 + {% endfor %}
  46 + </tbody>
  47 + </table>
  48 +</div>
  49 +{% endblock %}
  50 +
  51 +{% block javascript %}
  52 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'ReactApp!' %}</h1>
  10 + <div class="btn-toolbar mb-2 mb-md-0">
  11 + <div class="btn-group mr-2">
  12 + <a href="{% url 'reactapp:update' book.id %}" class="btn btn-sm btn-outline-secondary">{% trans 'Editar' %}</a>
  13 + <a href="{% url 'reactapp:delete' book.id %}" class="btn btn-sm btn-outline-secondary">{% trans 'Borrar' %}</a>
  14 + </div>
  15 + <a href="{% url 'reactapp:index' %}" class="btn btn-sm btn-outline-secondary">
  16 + {% trans 'Listar' %}
  17 + </a>
  18 + </div>
  19 +</div>
  20 +<div class="col">
  21 + <p>#{{ book.id }} {{ book.title }}</p>
  22 +
  23 +</div>
  24 +{% endblock %}
  25 +
  26 +{% block javascript %}
  27 +{% endblock %}
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'ReactApp!' %}</h1>
  10 +</div>
  11 +<div class="col">
  12 + <form method="POST">
  13 + {% csrf_token %}
  14 + <!-- title -->
  15 + <div class="form-group {% if form.title.errors %} has-error {% endif %}">
  16 + {{ form.title.label_tag }}
  17 + {{ form.title }}
  18 + {% if form.title.help_text %}
  19 + <span class="help-block">{{ form.title.help_text }}</span>
  20 + {% endif %}
  21 + {% for error in form.title.errors %}
  22 + <span class="help-block">{{ error }}</span>
  23 + {% endfor %}
  24 + </div>
  25 + <div class="d-flex justify-content-between">
  26 + <a href="{%url 'reactapp:index' %}" class="btn btn-outline-danger">{% trans 'Volver' %} </a>
  27 + <button type="submit" class="btn btn-primary">{% trans "Actualizar" %}</button>
  28 + </div>
  29 + </form>
  30 +</div>
  31 +{% endblock %}
  32 +
  33 +{% block javascript %}
  34 +{% endblock %}
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
  1 +from django.urls import path, include
  2 +
  3 +from . import views
  4 +
  5 +app_name = 'reactapp'
  6 +urlpatterns = [
  7 +
  8 + path('reactapp/', include(([
  9 + path('', views.index, name='index'),
  10 + path('create', views.create, name='create'),
  11 + path('read/<book_id>', views.read, name='read'),
  12 + path('update/<book_id>', views.update, name='update'),
  13 + path('delete/<book_id>', views.delete, name='delete'),
  14 + ])))
  15 +
  16 +
  17 +]
  1 +from django.shortcuts import render, redirect
  2 +
  3 +from .forms import BookForm
  4 +from .models import Book
  5 +
  6 +
  7 +def index(request):
  8 + """
  9 + -- INDEX --
  10 + :param request:
  11 + :return:
  12 + """
  13 + books = Book.objects.all()
  14 +
  15 + return render(request, 'reactapp/index.html', {
  16 + 'books': books
  17 + })
  18 +
  19 +
  20 +def create(request):
  21 + """
  22 + -- CREATE --
  23 + :param request:
  24 + :return:
  25 + """
  26 +
  27 + form = BookForm()
  28 +
  29 + if request.method == 'POST':
  30 + form = BookForm(request.POST)
  31 + if form.is_valid():
  32 + instance = form.save(commit=False)
  33 + instance.save()
  34 +
  35 + return redirect('reactapp:index')
  36 +
  37 + return render(request, 'reactapp/create.html', {
  38 + 'form': form
  39 + })
  40 +
  41 +
  42 +def read(request, book_id):
  43 + """
  44 + -- READ --
  45 + :param request:
  46 + :param book_id:
  47 + :return:
  48 + """
  49 + book = Book.objects.get(pk=book_id)
  50 +
  51 + return render(request, 'reactapp/read.html', {
  52 + 'book': book
  53 + })
  54 +
  55 +
  56 +def update(request, book_id):
  57 + """
  58 + -- CREATE --
  59 + :param request:
  60 + :param book_id:
  61 + :return:
  62 + """
  63 +
  64 + book = Book.objects.get(pk=book_id)
  65 +
  66 + form = BookForm(instance=book)
  67 +
  68 + if request.method == 'POST':
  69 + form = BookForm(request.POST, instance=book)
  70 + if form.is_valid():
  71 + instance = form.save(commit=False)
  72 + instance.save()
  73 +
  74 + return redirect('reactapp:index')
  75 +
  76 + return render(request, 'reactapp/update.html', {
  77 + 'form': form
  78 + })
  79 +
  80 +
  81 +def delete(request, book_id):
  82 + """
  83 + -- DELETE --
  84 + :param request:
  85 + :param book_id:
  86 + :return:
  87 + """
  88 +
  89 + Book.objects.get(pk=book_id).delete()
  90 +
  91 + return redirect('reactapp:index')
  1 +from django.contrib import admin
  2 +
  3 +# Register your models here.
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class WelcomeConfig(AppConfig):
  5 + name = 'apps.welcome'
  1 +from django.db import models
  2 +
  3 +# Create your models here.
  1 +{% extends 'core/base.html' %}
  2 +{% load static i18n %}
  3 +
  4 +{% block css %}
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
  9 + <h1 class="h2">{% trans 'Welcome!' %}</h1>
  10 +</div>
  11 +<div class="d-flex justify-content-center">
  12 + <a href="/admin"><img src="{% static 'welcome/images/pulpo.png' %}" class="img-fluid"></a>
  13 +</div>
  14 +{% endblock %}
  15 +
  16 +{% block javascript %}
  17 +{% endblock %}
  1 +from django.test import TestCase
  2 +
  3 +
  4 +class WelcomeFormTest(TestCase):
  5 + pass
  1 +from django.test import TestCase
  2 +
  3 +
  4 +class WelcomeModelTest(TestCase):
  5 +
  6 + @classmethod
  7 + def setUpTestData(cls):
  8 + """Set up non-modified objects used by all test methods."""
  9 + # User.objects.create(first_name='Big', last_name='Bob')
  10 + pass
  1 +from django.test import TestCase
  2 +
  3 +
  4 +# Create your tests here.
  5 +from django.urls import reverse
  6 +
  7 +
  8 +class WelcomeIndexTest(TestCase):
  9 + @classmethod
  10 + def setUpTestData(cls):
  11 + # print("setUpTestData: Run once to set up non-modified data for all class methods.")
  12 + pass
  13 +
  14 + def setUp(self):
  15 + # print("setUp: Run once for every test method to setup clean data.")
  16 + pass
  17 +
  18 + def test_view_url_exists_at_desired_location(self):
  19 + response = self.client.get('/')
  20 + self.assertEqual(response.status_code, 200)
  21 +
  22 + def test_view_url_accessible_by_name(self):
  23 + response = self.client.get(reverse('welcome:index'))
  24 + self.assertEqual(response.status_code, 200)
  25 +
  26 + def test_view_uses_correct_template(self):
  27 + response = self.client.get(reverse('welcome:index'))
  28 + self.assertEqual(response.status_code, 200)
  29 + self.assertTemplateUsed(response, 'welcome/index.html')
  1 +from django.urls import path
  2 +
  3 +from . import views
  4 +
  5 +app_name = 'welcome'
  6 +
  7 +urlpatterns = [
  8 +
  9 + path('', views.index, name='index'),
  10 +]
  1 +from django.shortcuts import render
  2 +
  3 +
  4 +def index(request):
  5 +
  6 + return render(request, 'welcome/index.html')
  1 +"""
  2 +ASGI config.
  3 +
  4 +It exposes the ASGI callable as a module-level variable named ``application``.
  5 +
  6 +For more information on this file, see
  7 +https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
  8 +"""
  9 +
  10 +import os
  11 +
  12 +from django.core.asgi import get_asgi_application
  13 +
  14 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings')
  15 +
  16 +application = get_asgi_application()
  1 +"""
  2 +Django settings.
  3 +
  4 +Generated by 'django-admin startproject' using Django 3.0.5.
  5 +
  6 +For more information on this file, see
  7 +https://docs.djangoproject.com/en/3.0/topics/settings/
  8 +
  9 +For the full list of settings and their values, see
  10 +https://docs.djangoproject.com/en/3.0/ref/settings/
  11 +"""
  12 +
  13 +from django.utils.translation import ugettext_lazy as _
  14 +import os
  15 +
  16 +# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
  17 +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  18 +
  19 +
  20 +# Quick-start development settings - unsuitable for production
  21 +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
  22 +
  23 +SECRET_KEY = os.environ.get("SECRET_KEY")
  24 +
  25 +DEBUG = int(os.environ.get("DEBUG", default=0))
  26 +
  27 +# 'DJANGO_ALLOWED_HOSTS' should be a single string of hosts with a space between each.
  28 +# For example: 'DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]'
  29 +ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS").split(" ")
  30 +
  31 +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
  32 +
  33 +# Application definition
  34 +
  35 +INSTALLED_APPS = [
  36 + "django.contrib.admin",
  37 + "django.contrib.auth",
  38 + "django.contrib.contenttypes",
  39 + "django.contrib.sessions",
  40 + "django.contrib.messages",
  41 + "django.contrib.staticfiles",
  42 +
  43 + # external apps
  44 + 'rosetta',
  45 +
  46 + "apps.core.apps.CoreConfig",
  47 + "apps.welcome.apps.WelcomeConfig",
  48 + "apps.djangoapp.apps.DjangoappConfig",
  49 + "apps.reactapp.apps.ReactappConfig",
  50 +]
  51 +
  52 +MIDDLEWARE = [
  53 + 'django.middleware.security.SecurityMiddleware',
  54 + 'django.contrib.sessions.middleware.SessionMiddleware',
  55 + 'django.middleware.locale.LocaleMiddleware',
  56 + 'django.middleware.common.CommonMiddleware',
  57 + 'django.middleware.csrf.CsrfViewMiddleware',
  58 + 'django.contrib.auth.middleware.AuthenticationMiddleware',
  59 + 'django.contrib.messages.middleware.MessageMiddleware',
  60 + 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  61 +]
  62 +
  63 +ROOT_URLCONF = 'conf.urls'
  64 +
  65 +TEMPLATES = [
  66 + {
  67 + 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  68 + 'DIRS': [],
  69 + 'APP_DIRS': True,
  70 + 'OPTIONS': {
  71 + 'context_processors': [
  72 + 'django.template.context_processors.debug',
  73 + 'django.template.context_processors.i18n',
  74 + 'django.template.context_processors.request',
  75 + 'django.contrib.auth.context_processors.auth',
  76 + 'django.contrib.messages.context_processors.messages',
  77 + ],
  78 + },
  79 + },
  80 +]
  81 +
  82 +WSGI_APPLICATION = 'conf.wsgi.application'
  83 +
  84 +
  85 +# Database
  86 +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
  87 +
  88 +DATABASES = {
  89 + "default": {
  90 + "ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"),
  91 + "NAME": os.environ.get("SQL_DATABASE", os.path.join(BASE_DIR, "db.sqlite3")),
  92 + # "USER": os.environ.get("SQL_USER", "user"),
  93 + # "PASSWORD": os.environ.get("SQL_PASSWORD", "password"),
  94 + # "HOST": os.environ.get("SQL_HOST", "localhost"),
  95 + # "PORT": os.environ.get("SQL_PORT", "5432"),
  96 + # "OPTIONS": {
  97 + # 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
  98 + # }
  99 + }
  100 +}
  101 +
  102 +
  103 +# Password validation
  104 +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
  105 +
  106 +AUTH_PASSWORD_VALIDATORS = [
  107 + # {
  108 + # 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
  109 + # },
  110 + {
  111 + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
  112 + 'OPTIONS': {
  113 + 'min_length': 4,
  114 + }
  115 + },
  116 + # {
  117 + # 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
  118 + # },
  119 + # {
  120 + # 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
  121 + # },
  122 +]
  123 +
  124 +
  125 +# Internationalization
  126 +# https://docs.djangoproject.com/en/3.0/topics/i18n/
  127 +
  128 +PREFIX_DEFAULT_LANGUAGE = False
  129 +
  130 +LANGUAGE_CODE = 'es'
  131 +
  132 +TIME_ZONE = 'Europe/Madrid'
  133 +
  134 +USE_I18N = True
  135 +
  136 +USE_L10N = True
  137 +
  138 +USE_TZ = False
  139 +
  140 +USE_THOUSAND_SEPARATOR = True
  141 +
  142 +LOCALE_PATHS = (
  143 + os.path.join(BASE_DIR, 'locale'),
  144 +)
  145 +
  146 +LANGUAGES = (
  147 + ('en', _('English')),
  148 + ('es', _('Español')),
  149 +)
  150 +
  151 +# Rosetta
  152 +ROSETTA_WSGI_AUTO_RELOAD = True
  153 +ROSETTA_SHOW_AT_ADMIN_PANEL = True
  154 +
  155 +# Static files (CSS, JavaScript, Images)
  156 +# https://docs.djangoproject.com/en/3.0/howto/static-files/
  157 +
  158 +MEDIA_URL = '/media/'
  159 +MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  160 +
  161 +STATIC_URL = '/static/'
  162 +STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  163 +
  1 +from django.conf.urls.i18n import i18n_patterns
  2 +from django.contrib import admin
  3 +from django.urls import path, include
  4 +from django.conf import settings
  5 +from django.conf.urls.static import static
  6 +
  7 +
  8 +urlpatterns = [
  9 + path('admin/', admin.site.urls),
  10 + path('i18n/', include('django.conf.urls.i18n')),
  11 +]
  12 +
  13 +if bool(settings.DEBUG):
  14 + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  15 +
  16 +# Rosetta Plugin Translate
  17 +if 'rosetta' in settings.INSTALLED_APPS:
  18 + urlpatterns += [
  19 + path('rosetta/', include('rosetta.urls'))
  20 + ]
  21 +
  22 +urlpatterns += i18n_patterns(
  23 + # Apps:
  24 + # Welcome
  25 + path('', include('apps.welcome.urls', namespace='welcome')),
  26 + # DjangoApp
  27 + path('', include('apps.djangoapp.urls', namespace='djangoapp')),
  28 + # ReactApp
  29 + path('', include('apps.reactapp.urls', namespace='reactapp')),
  30 +
  31 + # Show hide LANGUAGE_CODE in URL
  32 + prefix_default_language=settings.PREFIX_DEFAULT_LANGUAGE
  33 +)
  1 +"""
  2 +WSGI config.
  3 +
  4 +It exposes the WSGI callable as a module-level variable named ``application``.
  5 +
  6 +For more information on this file, see
  7 +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
  8 +"""
  9 +
  10 +import os
  11 +
  12 +from django.core.wsgi import get_wsgi_application
  13 +
  14 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings')
  15 +
  16 +application = get_wsgi_application()
  1 +#!/bin/sh
  2 +
  3 +if [ "$DATABASE" = "postgres" ]
  4 +then
  5 + echo "Waiting for postgres..."
  6 +
  7 + while ! nc -z $SQL_HOST $SQL_PORT; do
  8 + sleep 0.1
  9 + done
  10 +
  11 + echo "PostgreSQL started"
  12 +fi
  13 +
  14 +if [ "$DATABASE" = "mysql" ]
  15 +then
  16 + echo "Waiting for mysql..."
  17 +
  18 + while ! nc -z $SQL_HOST $SQL_PORT; do
  19 + sleep 0.1
  20 + done
  21 +
  22 + echo "MySQL started"
  23 +fi
  24 +
  25 +#python manage.py flush --no-input
  26 +python manage.py migrate
  27 +
  28 +exec "$@"
  1 +#!/usr/bin/env python
  2 +"""Django's command-line utility for administrative tasks."""
  3 +import os
  4 +import sys
  5 +
  6 +
  7 +def main():
  8 + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings')
  9 + try:
  10 + from django.core.management import execute_from_command_line
  11 + except ImportError as exc:
  12 + raise ImportError(
  13 + "Couldn't import Django. Are you sure it's installed and "
  14 + "available on your PYTHONPATH environment variable? Did you "
  15 + "forget to activate a virtual environment?"
  16 + ) from exc
  17 + execute_from_command_line(sys.argv)
  18 +
  19 +
  20 +if __name__ == '__main__':
  21 + main()
  1 +Django==3.1.6
  2 +gunicorn==20.0.4
  3 +django-rosetta==0.9.5
  1 +version: '3.8'
  2 +
  3 +services:
  4 +
  5 + web:
  6 + build: ./app
  7 + command: python manage.py runserver 0.0.0.0:8001
  8 + volumes:
  9 + - ./app/:/usr/src/app/
  10 + ports:
  11 + - 8001:8001
  12 + env_file:
  13 + - ./.env