Commit inicial

Habia trabajado un buen poco pero como vi que tenia que separar los
repositorios perdi bastante la historia :c
This commit is contained in:
Daniel Cortes
2020-05-22 00:05:27 -04:00
commit 3568abfbc7
44 changed files with 1841 additions and 0 deletions

0
users/__init__.py Normal file
View File

88
users/admin.py Normal file
View File

@@ -0,0 +1,88 @@
from django.contrib import admin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import Group
from django import forms
from users.models import User, SocialNetworks
admin.site.unregister(Group)
class UserAddForm(forms.ModelForm):
password = forms.CharField(label='Contraseña', widget=forms.PasswordInput)
password_confirmation = forms.CharField(label='Confirmación de contraseña', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'email')
def clean_password_confirmation(self):
password = self.cleaned_data.get("password")
password_confirmation = self.cleaned_data.get("password_confirmation")
if password and password_confirmation and password != password_confirmation:
raise forms.ValidationError('Las contraseñas no coinciden')
return password_confirmation
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('username', 'email', 'password', 'is_admin')
def clean_password(self):
return self.initial['password']
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
form = UserChangeForm
add_form = UserAddForm
list_display = ('username', 'email', 'is_admin', 'is_active')
list_filter = ('is_admin', 'is_active')
fieldsets = (
(None, {'fields': ('username', 'email', 'password')}),
('Permisos', {'fields': ('is_admin',)}),
)
add_fieldsets = (
(None, {'fields': ('username', 'email', 'password', 'password_confirmation')}),
)
search_fields = ('username', 'email')
ordering = ('username', 'email')
def get_fieldsets(self, request, obj=None):
if not obj:
return self.add_fieldsets
return super().get_fieldsets(request, obj)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super().get_form(request, obj, **defaults)
@admin.register(SocialNetworks)
class SocialNetworksAdmin(admin.ModelAdmin):
list_display = ('user', 'twitter', 'facebook', 'instagram', 'youtube', 'twitch')
raw_id_fields = ('user',)
search_fields = ('user__username',)
ordering = ('user',)

5
users/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'

View File

@@ -0,0 +1,34 @@
# Generated by Django 3.0.6 on 2020-05-16 02:58
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.manager
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(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')),
('username', models.CharField(max_length=40, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()])),
('email', models.EmailField(max_length=254)),
('is_active', models.BooleanField(default=True)),
('is_admin', models.BooleanField(default=False)),
],
options={
'abstract': False,
},
managers=[
('object', django.db.models.manager.Manager()),
],
),
]

View File

@@ -0,0 +1,56 @@
# Generated by Django 3.0.6 on 2020-05-16 06:01
from django.conf import settings
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='SocialNetworks',
fields=[
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('twitter', models.CharField(max_length=255, verbose_name='twitter')),
('facebook', models.CharField(max_length=255, verbose_name='facebook')),
('instagram', models.CharField(max_length=255, verbose_name='instagram')),
('youtube', models.CharField(max_length=255, verbose_name='youtube')),
('twitch', models.CharField(max_length=255, verbose_name='twitch')),
],
),
migrations.AlterModelOptions(
name='user',
options={'verbose_name': 'usuario', 'verbose_name_plural': 'usuarios'},
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, verbose_name='correo'),
),
migrations.AlterField(
model_name='user',
name='is_active',
field=models.BooleanField(default=True, verbose_name='esta activo'),
),
migrations.AlterField(
model_name='user',
name='is_admin',
field=models.BooleanField(default=False, verbose_name='es administrador'),
),
migrations.AlterField(
model_name='user',
name='password',
field=models.CharField(max_length=128, verbose_name='contraseña'),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(max_length=40, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='nombre de usuario'),
),
]

View File

@@ -0,0 +1,42 @@
# Generated by Django 3.0.6 on 2020-05-16 06:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0002_auto_20200516_0201'),
]
operations = [
migrations.AlterModelOptions(
name='socialnetworks',
options={'verbose_name': 'Redes Sociales', 'verbose_name_plural': 'Redes Sociales'},
),
migrations.AlterField(
model_name='socialnetworks',
name='facebook',
field=models.CharField(blank=True, max_length=255, verbose_name='facebook'),
),
migrations.AlterField(
model_name='socialnetworks',
name='instagram',
field=models.CharField(blank=True, max_length=255, verbose_name='instagram'),
),
migrations.AlterField(
model_name='socialnetworks',
name='twitch',
field=models.CharField(blank=True, max_length=255, verbose_name='twitch'),
),
migrations.AlterField(
model_name='socialnetworks',
name='twitter',
field=models.CharField(blank=True, max_length=255, verbose_name='twitter'),
),
migrations.AlterField(
model_name='socialnetworks',
name='youtube',
field=models.CharField(blank=True, max_length=255, verbose_name='youtube'),
),
]

78
users/models.py Normal file
View File

@@ -0,0 +1,78 @@
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, username, email, password):
if not username:
raise ValueError('El usuario debe tener un nombre de usuario')
if not email:
raise ValueError('El usuario debe tener un email')
if not password:
raise ValueError('El usuario debe tener una contraseña')
user = self.model(
username=self.model.normalize_username(username),
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password=None):
user = self.create_user(
username=username,
email=email,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
class Meta:
verbose_name = 'usuario'
verbose_name_plural = 'usuarios'
object = UserManager()
username_validator = UnicodeUsernameValidator()
username = models.CharField('nombre de usuario', max_length=40, validators=[username_validator], unique=True)
email = models.EmailField('correo')
is_active = models.BooleanField('esta activo', default=True)
is_admin = models.BooleanField('es administrador', default=False)
password = models.CharField('contraseña', max_length=128)
USERNAME_FIELD = 'username'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['email']
def has_perm(self, *args, **kwargs):
return True
def has_module_perms(self, *args, **kwargs):
return True
@property
def is_staff(self):
return self.is_admin
class SocialNetworks(models.Model):
class Meta:
verbose_name = 'Redes Sociales'
verbose_name_plural = 'Redes Sociales'
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
twitter = models.CharField('twitter', max_length=255, blank=True)
facebook = models.CharField('facebook', max_length=255, blank=True)
instagram = models.CharField('instagram', max_length=255, blank=True)
youtube = models.CharField('youtube', max_length=255, blank=True)
twitch = models.CharField('twitch', max_length=255, blank=True)
def __str__(self):
return f'{self._meta.verbose_name_plural} de {self.user.username}'

View File

@@ -0,0 +1,88 @@
:root {
--background-color: hsl(10, 20%, 98%);
--foreground-color: hsl(10, 10%, 13%);
--primary-color: hsl(284, 30%, 40%);
--highlight-color: hsl(290, 86%, 43%);
--light-color: hsl(10, 10%, 40%);
--error-color: hsl(0, 100%, 60%)
}
body {
background-color: var(--background-color);
color: var(--foreground-color);
overflow-y: scroll;
font-family: sans-serif;
}
a {
color: var(--highlight-color)
}
.login-box {
margin: 100px auto;
width: 45ch;
border: 1px solid #eaeaea;
border-radius: 4px;
overflow: hidden;
}
.login-box .heading {
display: flex;
align-items: center;
background: var(--primary-color);
color: var(--background-color);
height: 3em;
justify-content: center;
}
.login-box .form {
background-color: white;
padding: 20px;
}
.login-box .form a {
display: inline-block;
margin-top: 1em;
}
.error {
background-color: var(--error-color);
border: 1px rgba(234, 234, 234, .4) solid;
border-radius: 4px;
margin-bottom: 1em;
}
.error p {
text-align: center;
font-weight: bold;
color: var(--background-color);
}
label {
display: block;
margin-bottom: .3em;
font-size: .9em;
}
input[type='text'], input[type='password'], input[type='email'] {
display: block;
padding: 8px;
width: 100%;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 1.3em;
font-size: .9em;
}
.submit {
display: block;
width: 10ch;
height: 2.3em;
margin: .4em auto;
border: 1px solid rgba(234, 234, 234, .3);
border-radius: 8px;
color: var(--background-color);
background-color: var(--primary-color);
font-size: .9em;
}

4
users/static/users/css/normalize.css vendored Normal file
View File

@@ -0,0 +1,4 @@
/******************************************************************************
=> NORMALIZE.CSS
*******************************************************************************/
button,hr,input{overflow:visible}progress,sub,sup{vertical-align:baseline}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{color:inherit;display:table;max-width:100%;white-space:normal}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}

View File

@@ -0,0 +1,20 @@
{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
<title>{% block title %}MusicList{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% static "users/css/normalize.css" %}">
<link rel="stylesheet" type="text/css" href="{% static "users/css/base.css" %}">
{% block extrastyle %}{% endblock %}
{% block extrahead %}{% endblock %}
{% block responsive %}
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
</head>
<body>
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>

View File

@@ -0,0 +1,29 @@
{% extends "users/base.html" %}
{% block content %}
<div class="login-box">
<div class="heading">
<p>MusicList Login</p>
</div>
<div class="form">
{% if error %}
<div class="error">
<p>{{ error }}</p>
</div>
{% endif %}
<form method="POST" action="{% url 'auth:login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<label for="username">Usuario:</label>
<input type="text" name="username" id="username" required>
<label for="password">Contraseña:</label>
<input type="password" name="password" id="password" required>
<button class="submit" type="submit">Entrar</button>
</form>
<a href="{% url 'auth:register' %}?next={{ next|urlencode }}">¿No tienes cuenta?</a>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends "users/base.html" %}
{% block content %}
<div class="login-box">
<div class="heading">
<p>MusicList Registration</p>
</div>
<div class="form">
{% if error %}
<div class="error">
<p>{{ error }}</p>
</div>
{% endif %}
<form method="POST" action="{% url 'auth:register' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<label for="username">Usuario:</label>
<input type="text" name="username" id="username" {% if old.username %} value="{{ old.username }}" {% endif%} required>
<label for="email">Email:</label>
<input type="email" name="email" id="email" {% if old.email %} value="{{ old.email}}" {% endif%}required>
<label for="password">Contraseña:</label>
<input type="password" name="password" id="password" required>
<label for="password_confirm">Confirme Contraseña:</label>
<input type="password" name="password_confirm" id="password_confirm" required>
<button class="submit" type="submit">Entrar</button>
</form>
</div>
</div>
{% endblock %}

10
users/urls.py Normal file
View File

@@ -0,0 +1,10 @@
from django.urls import path
from users import views
app_name = 'auth'
urlpatterns = [
path('login/', views.login, name='login'),
path('logout/', views.logout, name='logout'),
path('register/', views.register, name='register')
]

102
users/views.py Normal file
View File

@@ -0,0 +1,102 @@
from django.http import HttpResponseRedirect, HttpResponseNotAllowed
from django.shortcuts import render
from django.utils.http import url_has_allowed_host_and_scheme
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout, get_user_model
def get_next_url(request):
next_url = request.POST.get('next', request.GET.get('next', ''))
url_is_safe = url_has_allowed_host_and_scheme(
url=next_url,
allowed_hosts=request.get_host(),
require_https=request.is_secure(),
)
print(next_url if url_is_safe else '/')
return next_url if url_is_safe else '/'
def login(request):
if request.method == 'GET':
return _login_get(request)
elif request.method == 'POST':
return _login_post(request)
else:
return HttpResponseNotAllowed(permitted_methods=['GET', 'POST'])
def _login_get(request):
return render(request, template_name='users/login.html', context={'next': get_next_url(request)})
def _login_post(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(request, username=username, password=password)
if user is not None:
auth_login(request, user)
return HttpResponseRedirect(get_next_url(request))
else:
return render(
request,
template_name='users/login.html',
context={'next': get_next_url(request), 'error': 'Usuario o contraseña son incorrectos'}
)
def logout(request):
auth_logout(request)
return HttpResponseRedirect(get_next_url(request))
def register(request):
if request.method == 'GET':
return _register_get(request)
elif request.method == 'POST':
return _register_post(request)
else:
return HttpResponseNotAllowed(permitted_methods=['GET', 'POST'])
def _register_get(request):
return render(request, template_name='users/register.html', context={'next': get_next_url(request)})
def _register_post(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
password_confirm = request.POST.get('password_confirm', '')
email = request.POST.get('email', '')
old = {
'username': username,
'email': email
}
if not username:
return render(
request,
template_name='users/register.html',
context={'next': get_next_url(request), 'error': 'Debe ingresar un nombre de usuario', 'old': old}
)
if get_user_model().objects.filter(username=username).count() > 0:
return render(
request,
template_name='users/register.html',
context={'next': get_next_url(request), 'error': 'El nombre de usuario esta en uso', 'old': old}
)
if password and password != password_confirm:
return render(
request,
template_name='users/register.html',
context={'next': get_next_url(request), 'error': 'Las contraseñas no coinciden', 'old': old}
)
user = get_user_model().objects.create_user(username, email, password)
auth_login(request, user)
return HttpResponseRedirect(get_next_url(request))