diff --git a/AppsPy/RepostajesPy/servicios/instance/repostajes.db b/AppsPy/RepostajesPy/servicios/instance/repostajes.db
new file mode 100644
index 0000000..c11393a
Binary files /dev/null and b/AppsPy/RepostajesPy/servicios/instance/repostajes.db differ
diff --git a/Django-Auth/accounts/__init__.py b/Django-Auth/accounts/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Django-Auth/accounts/admin.py b/Django-Auth/accounts/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/Django-Auth/accounts/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/Django-Auth/accounts/apps.py b/Django-Auth/accounts/apps.py
new file mode 100644
index 0000000..3e3c765
--- /dev/null
+++ b/Django-Auth/accounts/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class AccountsConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'accounts'
diff --git a/Django-Auth/accounts/migrations/__init__.py b/Django-Auth/accounts/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Django-Auth/accounts/models.py b/Django-Auth/accounts/models.py
new file mode 100644
index 0000000..71a8362
--- /dev/null
+++ b/Django-Auth/accounts/models.py
@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.
diff --git a/Django-Auth/accounts/tests.py b/Django-Auth/accounts/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/Django-Auth/accounts/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/Django-Auth/accounts/urls.py b/Django-Auth/accounts/urls.py
new file mode 100644
index 0000000..a10de07
--- /dev/null
+++ b/Django-Auth/accounts/urls.py
@@ -0,0 +1,9 @@
+# accounts/urls.py
+from django.urls import path
+
+from .views import SignUpView
+
+
+urlpatterns = [
+ path("signup/", SignUpView.as_view(), name="signup"),
+]
diff --git a/Django-Auth/accounts/views.py b/Django-Auth/accounts/views.py
new file mode 100644
index 0000000..496c56e
--- /dev/null
+++ b/Django-Auth/accounts/views.py
@@ -0,0 +1,10 @@
+# accounts/views.py
+from django.contrib.auth.forms import UserCreationForm
+from django.urls import reverse_lazy
+from django.views.generic import CreateView
+
+
+class SignUpView(CreateView):
+ form_class = UserCreationForm
+ success_url = reverse_lazy("login")
+ template_name = "registration/signup.html"
diff --git a/Django-Auth/db.sqlite3 b/Django-Auth/db.sqlite3
new file mode 100644
index 0000000..134d295
Binary files /dev/null and b/Django-Auth/db.sqlite3 differ
diff --git a/Django-Auth/django_project/__init__.py b/Django-Auth/django_project/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/Django-Auth/django_project/asgi.py b/Django-Auth/django_project/asgi.py
new file mode 100644
index 0000000..389abd2
--- /dev/null
+++ b/Django-Auth/django_project/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for django_project project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
+
+application = get_asgi_application()
diff --git a/Django-Auth/django_project/settings.py b/Django-Auth/django_project/settings.py
new file mode 100644
index 0000000..1f32f68
--- /dev/null
+++ b/Django-Auth/django_project/settings.py
@@ -0,0 +1,131 @@
+"""
+Django settings for django_project project.
+
+Generated by 'django-admin startproject' using Django 5.1.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/5.1/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-0ji$fkweyrscq+#fg73b+him8zsw26$7!iaxvy^rpy^)h#=br$'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'accounts',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'django_project.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ "DIRS": [BASE_DIR / "templates"], # new
+ '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',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'django_project.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/5.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/5.1/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+
+# django_project/settings.py
+LOGIN_REDIRECT_URL = "home" # new
+LOGOUT_REDIRECT_URL = "home" # new
+
+# django_project/settings.py
+EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # new
diff --git a/Django-Auth/django_project/urls.py b/Django-Auth/django_project/urls.py
new file mode 100644
index 0000000..0723cb7
--- /dev/null
+++ b/Django-Auth/django_project/urls.py
@@ -0,0 +1,26 @@
+"""
+URL configuration for django_project project.
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/5.1/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+from django.views.generic.base import TemplateView # new
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+ path("accounts/", include("accounts.urls")), # new
+ path("accounts/", include("django.contrib.auth.urls")), # new
+ path("", TemplateView.as_view(template_name="home.html"), name="home"), # new
+]
diff --git a/Django-Auth/django_project/wsgi.py b/Django-Auth/django_project/wsgi.py
new file mode 100644
index 0000000..a62895a
--- /dev/null
+++ b/Django-Auth/django_project/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for django_project project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
+
+application = get_wsgi_application()
diff --git a/Django-Auth/manage.py b/Django-Auth/manage.py
new file mode 100755
index 0000000..5ffd8de
--- /dev/null
+++ b/Django-Auth/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Django-Auth/templates/base.html b/Django-Auth/templates/base.html
new file mode 100644
index 0000000..d557f0a
--- /dev/null
+++ b/Django-Auth/templates/base.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ {% block title %}Django Auth Tutorial{% endblock %}
+
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+
diff --git a/Django-Auth/templates/home.html b/Django-Auth/templates/home.html
new file mode 100644
index 0000000..fe92216
--- /dev/null
+++ b/Django-Auth/templates/home.html
@@ -0,0 +1,19 @@
+
+{% extends "base.html" %}
+
+{% block title %}Home{% endblock %}
+
+{% block content %}
+{% if user.is_authenticated %}
+Hi {{ user.username }}!
+Password Change
+
+{% else %}
+You are not logged in
+Password Reset
+Log In
+{% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/Django-Auth/templates/registration/logged_out.html b/Django-Auth/templates/registration/logged_out.html
new file mode 100644
index 0000000..e9a5545
--- /dev/null
+++ b/Django-Auth/templates/registration/logged_out.html
@@ -0,0 +1,14 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+
+{% block breadcrumbs %}{% endblock %}
+
+{% block nav-sidebar %}{% endblock %}
+
+{% block content %}
+
+{% translate "Thanks for spending some quality time with the web site today." %}
+
+{% translate 'Log in again' %}
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/login.html b/Django-Auth/templates/registration/login.html
new file mode 100644
index 0000000..b61d9ec
--- /dev/null
+++ b/Django-Auth/templates/registration/login.html
@@ -0,0 +1,68 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block extrastyle %}{{ block.super }}
+{{ form.media }}
+{% endblock %}
+
+{% block bodyclass %}{{ block.super }} login{% endblock %}
+
+{% block usertools %}{% endblock %}
+
+{% block nav-global %}{% endblock %}
+
+{% block nav-sidebar %}{% endblock %}
+
+{% block content_title %}{% endblock %}
+
+{% block nav-breadcrumbs %}{% endblock %}
+
+{% block content %}
+{% if form.errors and not form.non_field_errors %}
+
+{% blocktranslate count counter=form.errors.items|length %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktranslate %}
+
+{% endif %}
+
+{% if form.non_field_errors %}
+{% for error in form.non_field_errors %}
+
+ {{ error }}
+
+{% endfor %}
+{% endif %}
+
+
+
+{% if user.is_authenticated %}
+
+{% blocktranslate trimmed %}
+ You are authenticated as {{ username }}, but are not authorized to
+ access this page. Would you like to login to a different account?
+{% endblocktranslate %}
+
+{% endif %}
+
+
+
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/login_tutorial.html b/Django-Auth/templates/registration/login_tutorial.html
new file mode 100644
index 0000000..785b9a5
--- /dev/null
+++ b/Django-Auth/templates/registration/login_tutorial.html
@@ -0,0 +1,13 @@
+
+{% extends "base.html" %}
+
+{% block title %}Login{% endblock %}
+
+{% block content %}
+Log In
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_change_done.html b/Django-Auth/templates/registration/password_change_done.html
new file mode 100644
index 0000000..784ab37
--- /dev/null
+++ b/Django-Auth/templates/registration/password_change_done.html
@@ -0,0 +1,20 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+{% block userlinks %}
+ {% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}{% translate 'Documentation' %} / {% endif %}{% translate 'Change password' %} /
+
+ {% include "admin/color_theme_toggle.html" %}
+{% endblock %}
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+{% translate 'Your password was changed.' %}
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_change_form.html b/Django-Auth/templates/registration/password_change_form.html
new file mode 100644
index 0000000..fde2373
--- /dev/null
+++ b/Django-Auth/templates/registration/password_change_form.html
@@ -0,0 +1,64 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+{% block extrastyle %}{{ block.super }} {% endblock %}
+{% block userlinks %}
+ {% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}{% translate 'Documentation' %} / {% endif %} {% translate 'Change password' %} /
+
+ {% include "admin/color_theme_toggle.html" %}
+{% endblock %}
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_reset_complete.html b/Django-Auth/templates/registration/password_reset_complete.html
new file mode 100644
index 0000000..e6a383f
--- /dev/null
+++ b/Django-Auth/templates/registration/password_reset_complete.html
@@ -0,0 +1,17 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% translate "Your password has been set. You may go ahead and log in now." %}
+
+{% translate 'Log in' %}
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_reset_confirm.html b/Django-Auth/templates/registration/password_reset_confirm.html
new file mode 100644
index 0000000..a07645c
--- /dev/null
+++ b/Django-Auth/templates/registration/password_reset_confirm.html
@@ -0,0 +1,47 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block extrastyle %}{{ block.super }} {% endblock %}
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% if validlink %}
+
+{% translate "Please enter your new password twice so we can verify you typed it in correctly." %}
+
+
+
+{% else %}
+
+{% translate "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}
+
+{% endif %}
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_reset_done.html b/Django-Auth/templates/registration/password_reset_done.html
new file mode 100644
index 0000000..8b1971a
--- /dev/null
+++ b/Django-Auth/templates/registration/password_reset_done.html
@@ -0,0 +1,17 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% translate 'We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.' %}
+
+{% translate 'If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder.' %}
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/password_reset_email.html b/Django-Auth/templates/registration/password_reset_email.html
new file mode 100644
index 0000000..6482209
--- /dev/null
+++ b/Django-Auth/templates/registration/password_reset_email.html
@@ -0,0 +1,14 @@
+{% load i18n %}{% autoescape off %}
+{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}
+
+{% translate "Please go to the following page and choose a new password:" %}
+{% block reset_link %}
+{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
+{% endblock %}
+{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }}
+
+{% translate "Thanks for using our site!" %}
+
+{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %}
+
+{% endautoescape %}
diff --git a/Django-Auth/templates/registration/password_reset_form.html b/Django-Auth/templates/registration/password_reset_form.html
new file mode 100644
index 0000000..0edfea8
--- /dev/null
+++ b/Django-Auth/templates/registration/password_reset_form.html
@@ -0,0 +1,31 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block extrastyle %}{{ block.super }} {% endblock %}
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+{% translate 'Forgotten your password? Enter your email address below, and we’ll email instructions for setting a new one.' %}
+
+
+
+{% endblock %}
diff --git a/Django-Auth/templates/registration/signup.html b/Django-Auth/templates/registration/signup.html
new file mode 100644
index 0000000..5431eac
--- /dev/null
+++ b/Django-Auth/templates/registration/signup.html
@@ -0,0 +1,13 @@
+
+{% extends "base.html" %}
+
+{% block title %}Sign Up{% endblock %}
+
+{% block content %}
+Sign up
+
+{% endblock %}
diff --git a/Libros/README.md b/Libros/README.md
index 2c57502..5f1a71c 100644
--- a/Libros/README.md
+++ b/Libros/README.md
@@ -1 +1,12 @@
-https://learndjango.com/tutorials/django-login-and-logout-tutorial
\ No newline at end of file
+https://learndjango.com/tutorials/django-login-and-logout-tutorial
+
+
+Username: {{ user.username }}
+
+User Full name: {{ user.get_full_name }}
+
+User Group: {{ user.groups.all.0 }}
+
+Email: {{ user.email }}
+
+Session Started at: {{ user.last_login }}
\ No newline at end of file
diff --git a/Libros/biblioteca/biblioteca/settings.py b/Libros/biblioteca/biblioteca/settings.py
index 182854f..2c494ee 100644
--- a/Libros/biblioteca/biblioteca/settings.py
+++ b/Libros/biblioteca/biblioteca/settings.py
@@ -124,5 +124,5 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = '/accounts/login/'
-LOGIN_REDIRECT_URL = '/gestion/libros/'
-LOGOUT_REDIRECT_URL = '/accounts/login/'
\ No newline at end of file
+LOGIN_REDIRECT_URL = '/gestion/libros'
+LOGOUT_REDIRECT_URL = 'principal'
\ No newline at end of file
diff --git a/Libros/biblioteca/biblioteca/urls.py b/Libros/biblioteca/biblioteca/urls.py
index 6688707..7ae0424 100644
--- a/Libros/biblioteca/biblioteca/urls.py
+++ b/Libros/biblioteca/biblioteca/urls.py
@@ -19,6 +19,7 @@ from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth import views as auth_views
+from django.views.generic.base import TemplateView # new
urlpatterns = [
path('obreros/', admin.site.urls),
@@ -26,6 +27,7 @@ urlpatterns = [
path('accounts/login/', auth_views.LoginView.as_view(), name='login'),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
+ path("", TemplateView.as_view(template_name="gestion/index.html"), name="principal"), # new
]
diff --git a/Libros/biblioteca/db.sqlite3 b/Libros/biblioteca/db.sqlite3
index 4aa5919..7449b63 100644
Binary files a/Libros/biblioteca/db.sqlite3 and b/Libros/biblioteca/db.sqlite3 differ
diff --git a/Libros/biblioteca/gestion/templates/_branding.html b/Libros/biblioteca/gestion/templates/_branding.html
index d81d140..2e9653d 100644
--- a/Libros/biblioteca/gestion/templates/_branding.html
+++ b/Libros/biblioteca/gestion/templates/_branding.html
@@ -1,6 +1,6 @@
{% load static %}
\ No newline at end of file
diff --git a/Libros/biblioteca/gestion/templates/_cabecera.html b/Libros/biblioteca/gestion/templates/_cabecera.html
index 92b73b6..57b87e4 100644
--- a/Libros/biblioteca/gestion/templates/_cabecera.html
+++ b/Libros/biblioteca/gestion/templates/_cabecera.html
@@ -15,15 +15,37 @@
{% endif %}
-
- {% if user.is_authenticated %}
-
-
-
-
- {% endif %}
diff --git a/Libros/biblioteca/gestion/templates/gestion/index.html b/Libros/biblioteca/gestion/templates/gestion/index.html
new file mode 100644
index 0000000..f83ed47
--- /dev/null
+++ b/Libros/biblioteca/gestion/templates/gestion/index.html
@@ -0,0 +1,27 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+
+
Introducción
+
+
+
+
+
¡Bienvenido a la colección de libros!
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/Libros/biblioteca/gestion/urls.py b/Libros/biblioteca/gestion/urls.py
index 2edf7ba..f687dcc 100644
--- a/Libros/biblioteca/gestion/urls.py
+++ b/Libros/biblioteca/gestion/urls.py
@@ -1,16 +1,18 @@
from django.urls import path
+
from . import views
urlpatterns = [
- path('autores/', views.lista_autores, name='lista_autores'),
+ path('autores', views.lista_autores, name='lista_autores'),
path('autores/nuevo/', views.nuevo_autor, name='nuevo_autor'),
path('autores//', views.detalle_autor, name='detalle_autor'),
path('autores//editar/', views.editar_autor, name='editar_autor'),
path('autores//eliminar/', views.eliminar_autor, name='eliminar_autor'),
- path('libros/', views.lista_libros, name='lista_libros'),
+ path('libros', views.lista_libros, name='lista_libros'),
path('libros/nuevo/', views.nuevo_libro, name='nuevo_libro'),
path('libros//', views.detalle_libro, name='detalle_libro'),
path('libros//editar/', views.editar_libro, name='editar_libro'),
path('libros//eliminar/', views.eliminar_libro, name='eliminar_libro'),
+
]
diff --git a/Libros/biblioteca/gestion/views.py b/Libros/biblioteca/gestion/views.py
index 8df04ba..45d20ce 100644
--- a/Libros/biblioteca/gestion/views.py
+++ b/Libros/biblioteca/gestion/views.py
@@ -84,9 +84,3 @@ def eliminar_libro(request, libro_id):
libro = get_object_or_404(Libro, pk=libro_id)
libro.delete()
return redirect('lista_libros')
-
-
-@login_required
-def user_logout(request):
- logout(request)
- return render(request, 'registration/logged_out.html', {})
\ No newline at end of file
diff --git a/django-custom-model/Pipfile b/django-custom-model/Pipfile
new file mode 100644
index 0000000..42133fe
--- /dev/null
+++ b/django-custom-model/Pipfile
@@ -0,0 +1,13 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+django = "*"
+pillow = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.10"
diff --git a/django-custom-model/Pipfile.lock b/django-custom-model/Pipfile.lock
new file mode 100644
index 0000000..b925b6c
--- /dev/null
+++ b/django-custom-model/Pipfile.lock
@@ -0,0 +1,141 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "f0a6b10e1349a882b824f490c90a5b0b71905386b17672678bed9a1bfabb658a"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.10"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "asgiref": {
+ "hashes": [
+ "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47",
+ "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==3.8.1"
+ },
+ "django": {
+ "hashes": [
+ "sha256:848a5980e8efb76eea70872fb0e4bc5e371619c70fffbe48e3e1b50b2c09455d",
+ "sha256:d3b811bf5371a26def053d7ee42a9df1267ef7622323fe70a601936725aa4557"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.10'",
+ "version": "==5.1"
+ },
+ "pillow": {
+ "hashes": [
+ "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885",
+ "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea",
+ "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df",
+ "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5",
+ "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c",
+ "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d",
+ "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd",
+ "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06",
+ "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908",
+ "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a",
+ "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be",
+ "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0",
+ "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b",
+ "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80",
+ "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a",
+ "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e",
+ "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9",
+ "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696",
+ "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b",
+ "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309",
+ "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e",
+ "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab",
+ "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d",
+ "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060",
+ "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d",
+ "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d",
+ "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4",
+ "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3",
+ "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6",
+ "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb",
+ "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94",
+ "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b",
+ "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496",
+ "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0",
+ "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319",
+ "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b",
+ "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856",
+ "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef",
+ "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680",
+ "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b",
+ "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42",
+ "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e",
+ "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597",
+ "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a",
+ "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8",
+ "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3",
+ "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736",
+ "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da",
+ "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126",
+ "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd",
+ "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5",
+ "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b",
+ "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026",
+ "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b",
+ "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc",
+ "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46",
+ "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2",
+ "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c",
+ "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe",
+ "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984",
+ "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a",
+ "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70",
+ "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca",
+ "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b",
+ "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91",
+ "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3",
+ "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84",
+ "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1",
+ "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5",
+ "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be",
+ "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f",
+ "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc",
+ "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9",
+ "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e",
+ "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141",
+ "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef",
+ "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22",
+ "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27",
+ "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e",
+ "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"
+ ],
+ "index": "pypi",
+ "markers": "python_version >= '3.8'",
+ "version": "==10.4.0"
+ },
+ "sqlparse": {
+ "hashes": [
+ "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4",
+ "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==0.5.1"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+ "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
+ ],
+ "markers": "python_version < '3.11'",
+ "version": "==4.12.2"
+ }
+ },
+ "develop": {}
+}
diff --git a/django-custom-model/README.md b/django-custom-model/README.md
new file mode 100644
index 0000000..6b028e7
--- /dev/null
+++ b/django-custom-model/README.md
@@ -0,0 +1,3 @@
+## Fuente
+
+https://testdriven.io/blog/django-custom-user-model/
\ No newline at end of file
diff --git a/django-custom-model/customuser.sqlite3 b/django-custom-model/customuser.sqlite3
new file mode 100644
index 0000000..accc850
Binary files /dev/null and b/django-custom-model/customuser.sqlite3 differ
diff --git a/django-custom-model/db.sqlite3 b/django-custom-model/db.sqlite3
new file mode 100644
index 0000000..e69de29
diff --git a/django-custom-model/hola_django/__init__.py b/django-custom-model/hola_django/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/django-custom-model/hola_django/asgi.py b/django-custom-model/hola_django/asgi.py
new file mode 100644
index 0000000..a478ff8
--- /dev/null
+++ b/django-custom-model/hola_django/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for hola_django project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hola_django.settings')
+
+application = get_asgi_application()
diff --git a/django-custom-model/hola_django/settings.py b/django-custom-model/hola_django/settings.py
new file mode 100644
index 0000000..554faf0
--- /dev/null
+++ b/django-custom-model/hola_django/settings.py
@@ -0,0 +1,127 @@
+"""
+Django settings for hola_django project.
+
+Generated by 'django-admin startproject' using Django 5.1.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/5.1/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure--q$(z*_2n3!s%5&!g3v7!&w6q_26wmu$+9#p1hj2072f%58fw0'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'users',
+]
+
+MIDDLEWARE = [
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'hola_django.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ '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',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'hola_django.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'customuser.sqlite3',
+ }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/5.1/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/5.1/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+
+
+AUTH_USER_MODEL = "users.CustomUser"
\ No newline at end of file
diff --git a/django-custom-model/hola_django/urls.py b/django-custom-model/hola_django/urls.py
new file mode 100644
index 0000000..c873696
--- /dev/null
+++ b/django-custom-model/hola_django/urls.py
@@ -0,0 +1,22 @@
+"""
+URL configuration for hola_django project.
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/5.1/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+]
diff --git a/django-custom-model/hola_django/wsgi.py b/django-custom-model/hola_django/wsgi.py
new file mode 100644
index 0000000..f24a796
--- /dev/null
+++ b/django-custom-model/hola_django/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for hola_django project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hola_django.settings')
+
+application = get_wsgi_application()
diff --git a/django-custom-model/manage.py b/django-custom-model/manage.py
new file mode 100755
index 0000000..2fec864
--- /dev/null
+++ b/django-custom-model/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hola_django.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/django-custom-model/profile_images/foto.jpeg b/django-custom-model/profile_images/foto.jpeg
new file mode 100644
index 0000000..464838d
Binary files /dev/null and b/django-custom-model/profile_images/foto.jpeg differ
diff --git a/django-custom-model/temp.txt b/django-custom-model/temp.txt
new file mode 100644
index 0000000..a6def43
--- /dev/null
+++ b/django-custom-model/temp.txt
@@ -0,0 +1,38 @@
+Migrations for 'users':
+ users/migrations/0001_initial.py
+ + Create model CustomUser
+Full migrations file '0001_initial.py':
+# Generated by Django 5.1 on 2024-08-08 13:04
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('auth', '0012_alter_user_first_name_max_length'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CustomUser',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('password', models.CharField(max_length=128, verbose_name='password')),
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+ ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
+ ('is_staff', models.BooleanField(default=False)),
+ ('is_active', models.BooleanField(default=True)),
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now)),
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/django-custom-model/users/__init__.py b/django-custom-model/users/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/django-custom-model/users/admin.py b/django-custom-model/users/admin.py
new file mode 100644
index 0000000..64b847a
--- /dev/null
+++ b/django-custom-model/users/admin.py
@@ -0,0 +1,34 @@
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+
+from .forms import CustomUserCreationForm, CustomUserChangeForm
+from .models import CustomUser
+
+
+class CustomUserAdmin(UserAdmin):
+ add_form = CustomUserCreationForm
+ form = CustomUserChangeForm
+ model = CustomUser
+ list_display = ("email", "is_staff", "is_active","foto")
+ list_filter = ("email", "is_staff", "is_active",)
+ fieldsets = (
+ (None, {"fields": ("email", "password")}),
+ ("Permissions", {"fields": ("is_staff", "is_active", "groups", "user_permissions")}),
+ ("Varios", {"fields": ("foto",)}),
+ )
+ add_fieldsets = (
+ (None, {
+ "classes": ("wide",),
+ "fields": (
+ "email", "password1", "password2", "is_staff",
+ "is_active", "groups", "user_permissions"
+ )}
+ ),
+ ("Varios", {"fields": ("foto",)}),
+
+ )
+ search_fields = ("email",)
+ ordering = ("email",)
+
+
+admin.site.register(CustomUser, CustomUserAdmin)
diff --git a/django-custom-model/users/apps.py b/django-custom-model/users/apps.py
new file mode 100644
index 0000000..72b1401
--- /dev/null
+++ b/django-custom-model/users/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class UsersConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'users'
diff --git a/django-custom-model/users/forms.py b/django-custom-model/users/forms.py
new file mode 100644
index 0000000..2d3ceb7
--- /dev/null
+++ b/django-custom-model/users/forms.py
@@ -0,0 +1,17 @@
+from django.contrib.auth.forms import UserCreationForm, UserChangeForm
+
+from .models import CustomUser
+
+
+class CustomUserCreationForm(UserCreationForm):
+
+ class Meta:
+ model = CustomUser
+ fields = ("email", "foto")
+
+
+class CustomUserChangeForm(UserChangeForm):
+
+ class Meta:
+ model = CustomUser
+ fields = ("email", "foto")
diff --git a/django-custom-model/users/managers.py b/django-custom-model/users/managers.py
new file mode 100644
index 0000000..f441f74
--- /dev/null
+++ b/django-custom-model/users/managers.py
@@ -0,0 +1,34 @@
+from django.contrib.auth.base_user import BaseUserManager
+from django.utils.translation import gettext_lazy as _
+
+
+class CustomUserManager(BaseUserManager):
+ """
+ Custom user model manager where email is the unique identifiers
+ for authentication instead of usernames.
+ """
+ def create_user(self, email, password, **extra_fields):
+ """
+ Create and save a user with the given email and password.
+ """
+ if not email:
+ raise ValueError(_("The Email must be set"))
+ email = self.normalize_email(email)
+ user = self.model(email=email, **extra_fields)
+ user.set_password(password)
+ user.save()
+ return user
+
+ def create_superuser(self, email, password, **extra_fields):
+ """
+ Create and save a SuperUser with the given email and password.
+ """
+ extra_fields.setdefault("is_staff", True)
+ extra_fields.setdefault("is_superuser", True)
+ extra_fields.setdefault("is_active", True)
+
+ if extra_fields.get("is_staff") is not True:
+ raise ValueError(_("Superuser must have is_staff=True."))
+ if extra_fields.get("is_superuser") is not True:
+ raise ValueError(_("Superuser must have is_superuser=True."))
+ return self.create_user(email, password, **extra_fields)
diff --git a/django-custom-model/users/migrations/0001_initial.py b/django-custom-model/users/migrations/0001_initial.py
new file mode 100644
index 0000000..0dadda3
--- /dev/null
+++ b/django-custom-model/users/migrations/0001_initial.py
@@ -0,0 +1,34 @@
+# Generated by Django 5.1 on 2024-08-08 13:05
+
+import django.utils.timezone
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('auth', '0012_alter_user_first_name_max_length'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='CustomUser',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('password', models.CharField(max_length=128, verbose_name='password')),
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+ ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
+ ('is_staff', models.BooleanField(default=False)),
+ ('is_active', models.BooleanField(default=True)),
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now)),
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/django-custom-model/users/migrations/0002_customuser_foto.py b/django-custom-model/users/migrations/0002_customuser_foto.py
new file mode 100644
index 0000000..1464c09
--- /dev/null
+++ b/django-custom-model/users/migrations/0002_customuser_foto.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1 on 2024-08-08 13:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='customuser',
+ name='foto',
+ field=models.TextField(default=''),
+ ),
+ ]
diff --git a/django-custom-model/users/migrations/0003_alter_customuser_foto.py b/django-custom-model/users/migrations/0003_alter_customuser_foto.py
new file mode 100644
index 0000000..1cc1758
--- /dev/null
+++ b/django-custom-model/users/migrations/0003_alter_customuser_foto.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1 on 2024-08-08 13:29
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0002_customuser_foto'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='customuser',
+ name='foto',
+ field=models.ImageField(blank=True, upload_to='profile_images'),
+ ),
+ ]
diff --git a/django-custom-model/users/migrations/__init__.py b/django-custom-model/users/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/django-custom-model/users/models.py b/django-custom-model/users/models.py
new file mode 100644
index 0000000..47b2175
--- /dev/null
+++ b/django-custom-model/users/models.py
@@ -0,0 +1,22 @@
+from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
+from django.db import models
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+
+from .managers import CustomUserManager
+
+
+class CustomUser(AbstractBaseUser, PermissionsMixin):
+ email = models.EmailField(_("email address"), unique=True)
+ is_staff = models.BooleanField(default=False)
+ is_active = models.BooleanField(default=True)
+ date_joined = models.DateTimeField(default=timezone.now)
+ foto = models.ImageField(upload_to="profile_images", blank=True)
+
+ USERNAME_FIELD = "email"
+ REQUIRED_FIELDS = []
+
+ objects = CustomUserManager()
+
+ def __str__(self):
+ return self.email
diff --git a/django-custom-model/users/tests.py b/django-custom-model/users/tests.py
new file mode 100644
index 0000000..d5b2fce
--- /dev/null
+++ b/django-custom-model/users/tests.py
@@ -0,0 +1,42 @@
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+
+
+class UsersManagersTests(TestCase):
+
+ def test_create_user(self):
+ User = get_user_model()
+ user = User.objects.create_user(email="normal@user.com", password="foo")
+ self.assertEqual(user.email, "normal@user.com")
+ self.assertTrue(user.is_active)
+ self.assertFalse(user.is_staff)
+ self.assertFalse(user.is_superuser)
+ try:
+ # username is None for the AbstractUser option
+ # username does not exist for the AbstractBaseUser option
+ self.assertIsNone(user.username)
+ except AttributeError:
+ pass
+ with self.assertRaises(TypeError):
+ User.objects.create_user()
+ with self.assertRaises(TypeError):
+ User.objects.create_user(email="")
+ with self.assertRaises(ValueError):
+ User.objects.create_user(email="", password="foo")
+
+ def test_create_superuser(self):
+ User = get_user_model()
+ admin_user = User.objects.create_superuser(email="super@user.com", password="foo")
+ self.assertEqual(admin_user.email, "super@user.com")
+ self.assertTrue(admin_user.is_active)
+ self.assertTrue(admin_user.is_staff)
+ self.assertTrue(admin_user.is_superuser)
+ try:
+ # username is None for the AbstractUser option
+ # username does not exist for the AbstractBaseUser option
+ self.assertIsNone(admin_user.username)
+ except AttributeError:
+ pass
+ with self.assertRaises(ValueError):
+ User.objects.create_superuser(
+ email="super@user.com", password="foo", is_superuser=False)
diff --git a/django-custom-model/users/views.py b/django-custom-model/users/views.py
new file mode 100644
index 0000000..91ea44a
--- /dev/null
+++ b/django-custom-model/users/views.py
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.