Lucas
12 months ago
commit
c602d0bf8d
32 changed files with 1193 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||
venv |
|||
.venv |
|||
.env |
|||
*.sqlite3 |
|||
media |
|||
*__pycache__ |
|||
.idea |
|||
*.code-workspace |
|||
.report.json |
|||
report.json |
|||
.vscode |
@ -0,0 +1,26 @@ |
|||
Copyright (c) Lucas F. All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without modification, |
|||
are permitted provided that the following conditions are met: |
|||
|
|||
1. Redistributions of source code must retain the above copyright notice, |
|||
this list of conditions and the following disclaimer. |
|||
|
|||
2. Redistributions in binary form must reproduce the above copyright |
|||
notice, this list of conditions and the following disclaimer in the |
|||
documentation and/or other materials provided with the distribution. |
|||
|
|||
3. Neither the name of the copyright holder nor the names of its contributors may be used |
|||
to endorse or promote products derived from this software without |
|||
specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
|||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,40 @@ |
|||
.DEFAULT_GOAL = default |
|||
SCRIPT = contrib/scripts/make_scripts.sh |
|||
|
|||
## @ env
|
|||
.PHONY: env |
|||
env: ## creates a .env file
|
|||
@./${SCRIPT} make_env_file |
|||
|
|||
## @ task
|
|||
.PHONY: check run shell_plus clear_migrations show_migrations migrations migrate elements |
|||
check: ## same as manage.py check
|
|||
@./${SCRIPT} check |
|||
|
|||
run: ## same as manage.py run server
|
|||
@./${SCRIPT} run |
|||
|
|||
shell_plus: ## same as .manage.py shell_plus
|
|||
@./${SCRIPT} shell_plus |
|||
|
|||
clear_migrations: ## same as manage.py showmigrations
|
|||
@./${SCRIPT} show_migrations |
|||
|
|||
show_migrations: ## same as manage.py showmigrations
|
|||
@./${SCRIPT} show_migrations |
|||
|
|||
migrations: ## same as manage.py makemigrations
|
|||
@./${SCRIPT} migrations |
|||
|
|||
migrate: ## same as manage.py migrate
|
|||
@./${SCRIPT} migrate |
|||
|
|||
elements: ## create initial app elements
|
|||
@./${SCRIPT} elements |
|||
|
|||
## @ help
|
|||
.PHONY: help |
|||
help: ## display all make commands
|
|||
@./${SCRIPT} help $(MAKEFILE_LIST) |
|||
|
|||
default: help |
@ -0,0 +1,53 @@ |
|||
# Form User |
|||
|
|||
É um exemplo de aplicação Django para criação de modelo, tendo em vista salvar dois modelos ao mesmo tempo, |
|||
sendo que um deles possui relação do tipo muitos para muitos. |
|||
|
|||
|
|||
## Instalação |
|||
|
|||
via `pip` |
|||
|
|||
```bash |
|||
git clone https://git.lucasf.dev/public/form_user.git |
|||
cd form_user |
|||
python -m venv .venv |
|||
. ./.venv/bin/activate |
|||
pip install -r requirements.txt |
|||
``` |
|||
|
|||
|
|||
via `poetry` |
|||
|
|||
```bash |
|||
git clone https://git.lucasf.dev/public/form_user.git |
|||
cd form_user |
|||
poetry shell |
|||
poetry install |
|||
``` |
|||
|
|||
|
|||
## Criando arquivo .env |
|||
|
|||
```bash |
|||
make env |
|||
``` |
|||
|
|||
## Criando elementos para aplicação |
|||
|
|||
primeiro informe no arquivo `.env` gerado os valores para as seguintes variáveis: |
|||
|
|||
ADMIN_USERNAME |
|||
ADMIN_EMAIL |
|||
ADMIN_PASSWORD |
|||
|
|||
```bash |
|||
make elements |
|||
``` |
|||
|
|||
## Iniciar a aplicação |
|||
|
|||
```bash |
|||
make run |
|||
``` |
|||
|
@ -0,0 +1,122 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
HERE="$(cd "$(dirname "$0")" && pwd)" |
|||
BASEDIR="$(cd "$(dirname "$1")" && pwd)" |
|||
CHARS="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" |
|||
for ((i=0;i<${#CHARS};i++)); do ARRAY[$i]="${CHARS:i:1}"; done |
|||
MSG_SUCCESS="DONE!" |
|||
POETRY=0 |
|||
PYTHON=0 |
|||
|
|||
key_gen() { |
|||
for ((c=1; c<=50; c++)); do |
|||
KEY="$KEY${ARRAY[$((RANDOM % 50))]}" |
|||
done |
|||
echo $KEY |
|||
} |
|||
|
|||
make_env_file() { |
|||
if [[ ! -f ".env" ]]; then |
|||
ENV="SECRET_KEY='$(key_gen)'\n |
|||
ALLOWED_HOSTS=localhost, 10.0.2.2, 127.0.0.1\n |
|||
DEBUG=True\n\n |
|||
#DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5433/db\n\n |
|||
ADMIN_USERNAME=\n |
|||
ADMIN_EMAIL=\n |
|||
ADMIN_PASSWORD=\n\n |
|||
EMAIL_HOST=\n |
|||
EMAIL_PORT=\n |
|||
EMAIL_HOST_USER=\n |
|||
EMAIL_HOST_PASSWORD=\n |
|||
EMAIL_USE_TLS=True\n |
|||
DEFAULT_FROM_EMAIL= |
|||
" |
|||
|
|||
$(echo -e $ENV | sed -e 's/^[ \t]*//' > .env) |
|||
echo "ENV FILE - $MSG_SUCCESS" |
|||
fi |
|||
} |
|||
|
|||
verify_poetry() { |
|||
if command -v poetry &> /dev/null; then |
|||
POETRY=1 |
|||
fi |
|||
} |
|||
|
|||
verify_python() { |
|||
if command -v python3 &> /dev/null; then |
|||
PYTHON=1 |
|||
fi |
|||
} |
|||
|
|||
venv_name() { |
|||
if [[ -d "$BASEDIR/.venv" ]]; then |
|||
echo ".venv" |
|||
fi |
|||
if [[ -d "$BASEDIR/venv" ]]; then |
|||
echo "venv" |
|||
fi |
|||
} |
|||
|
|||
python_name() { |
|||
if [[ PYTHON -eq 1 ]]; then |
|||
echo "python3" |
|||
else |
|||
echo "python" |
|||
fi |
|||
} |
|||
|
|||
help() { |
|||
awk 'BEGIN {FS="## @ "; print "Usage: make";} /^## @ / { printf "\033[31m\n" substr($1, 5) "\n";} {FS=" ## ";} /^[a-zA-Z_-]+:.*? ##/ { print "\033[33m -", $1 "\033[37m", $2}' $ARG |
|||
} |
|||
|
|||
run() { |
|||
$(venv_name)/bin/$(python_name) manage.py runserver 0.0.0.0:8000 |
|||
} |
|||
|
|||
shell_plus() { |
|||
$(venv_name)/bin/$(python_name) manage.py shell_plus |
|||
} |
|||
|
|||
check() { |
|||
$(venv_name)/bin/$(python_name) manage.py check |
|||
IS_OK=$? |
|||
} |
|||
|
|||
show_migrations() { |
|||
$(venv_name)/bin/$(python_name) manage.py showmigrations |
|||
} |
|||
|
|||
migrations() { |
|||
$(venv_name)/bin/$(python_name) manage.py makemigrations |
|||
} |
|||
|
|||
migrate() { |
|||
$(venv_name)/bin/$(python_name) manage.py migrate |
|||
} |
|||
|
|||
check() { |
|||
$(venv_name)/bin/$(python_name) manage.py check |
|||
} |
|||
|
|||
elements() { |
|||
$(venv_name)/bin/$(python_name) manage.py elements |
|||
} |
|||
|
|||
clear_migrations() { |
|||
find $BASEDIR -path '*/migrations/*.py' -not -name '__init__.py' -not -path '*/.venv/*' -delete |
|||
find $BASEDIR -path '*/migrations/*.pyc' -not -name '__init__.py' -not -path '*/.venv/*' -delete |
|||
if [[ -f $BASEDIR/media/ ]]; then |
|||
rm $BASEDIR/media/* |
|||
fi |
|||
if [[ -f db.sqlite3 ]];then |
|||
rm db.sqlite3 |
|||
fi |
|||
podman pod rm -f cq_pod && |
|||
podman volume prune |
|||
} |
|||
|
|||
verify_python |
|||
verify_poetry |
|||
ARG=$2 |
|||
$1 |
@ -0,0 +1,64 @@ |
|||
from django.contrib import admin |
|||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin |
|||
from django.contrib.auth.forms import UserCreationForm |
|||
|
|||
from .forms import UserAdminForm |
|||
from .models import CustomUser, TagUnidade, Unidade, UsuarioPermissaoUnidadeTag |
|||
|
|||
|
|||
class UserAdmin(BaseUserAdmin): |
|||
add_form = UserCreationForm |
|||
add_fieldsets = ( |
|||
( |
|||
None, |
|||
{ |
|||
"classes": ("wide",), |
|||
"fields": ( |
|||
"username", |
|||
"email", |
|||
"password1", |
|||
"password2", |
|||
"is_active", |
|||
), |
|||
}, |
|||
), |
|||
) |
|||
form = UserAdminForm |
|||
fieldsets = ( |
|||
(None, {"fields": ("username", "email")}), |
|||
( |
|||
"Informações Básicas", |
|||
{ |
|||
"fields": ( |
|||
"first_name", |
|||
"last_login", |
|||
) |
|||
}, |
|||
), |
|||
( |
|||
"Permissões", |
|||
{ |
|||
"fields": ( |
|||
"is_active", |
|||
"is_staff", |
|||
"is_superuser", |
|||
"groups", |
|||
"user_permissions", |
|||
) |
|||
}, |
|||
), |
|||
) |
|||
list_display = [ |
|||
"username", |
|||
"first_name", |
|||
"email", |
|||
"is_active", |
|||
"is_staff", |
|||
"date_joined", |
|||
] |
|||
|
|||
|
|||
admin.site.register(CustomUser, UserAdmin) |
|||
admin.site.register(UsuarioPermissaoUnidadeTag) |
|||
admin.site.register(TagUnidade) |
|||
admin.site.register(Unidade) |
@ -0,0 +1,7 @@ |
|||
from django.apps import AppConfig |
|||
|
|||
|
|||
class ContasConfig(AppConfig): |
|||
default_auto_field = "django.db.models.BigAutoField" |
|||
name = "contas" |
|||
default = False |
@ -0,0 +1,28 @@ |
|||
from django import forms |
|||
from django.contrib.auth.forms import UserCreationForm |
|||
|
|||
from form_user.apps.contas.models import CustomUser, UsuarioPermissaoUnidadeTag |
|||
|
|||
|
|||
class UserAdminForm(forms.ModelForm): |
|||
class Meta: |
|||
model = CustomUser |
|||
fields = UserCreationForm.Meta.fields + ( |
|||
"username", |
|||
"email", |
|||
"first_name", |
|||
"is_active", |
|||
"is_staff", |
|||
) |
|||
|
|||
|
|||
class PermissaoForm(forms.ModelForm): |
|||
class Meta: |
|||
model = UsuarioPermissaoUnidadeTag |
|||
fields = ["tags", "unidades"] |
|||
|
|||
|
|||
class NewUserForm(forms.ModelForm): |
|||
class Meta: |
|||
model = CustomUser |
|||
fields = ["username"] |
@ -0,0 +1,33 @@ |
|||
from decouple import config |
|||
from django.core.management.base import BaseCommand |
|||
from django.utils.translation import gettext_lazy as _ |
|||
|
|||
from form_user.apps.contas.models import CustomUser |
|||
|
|||
|
|||
class AppException(Exception, BaseCommand): |
|||
def __init__(self, exception): |
|||
print( |
|||
BaseCommand().stdout.write( |
|||
BaseCommand().style.NOTICE(f"Error: {exception}") |
|||
) |
|||
) |
|||
|
|||
|
|||
class Command(BaseCommand): |
|||
help = _("Creates initial information for application") |
|||
su_created = False |
|||
|
|||
def handle(self, *args, **options): |
|||
if not CustomUser.objects.filter(username=config("ADMIN_USERNAME")): |
|||
su = CustomUser.objects.create_superuser( |
|||
username=config("ADMIN_USERNAME"), |
|||
email=config("ADMIN_EMAIL"), |
|||
password=config("ADMIN_PASSWORD"), |
|||
is_active=True, |
|||
is_staff=True, |
|||
) |
|||
if su: |
|||
self.stdout.write(self.style.SUCCESS(_("Superuser created!"))) |
|||
else: |
|||
self.stdout.write(self.style.NOTICE(_("Superuser already exists!"))) |
@ -0,0 +1,7 @@ |
|||
from django.contrib.auth.models import UserManager |
|||
from django.db.models import Q |
|||
|
|||
|
|||
class CustomUserManager(UserManager): |
|||
def all(self): |
|||
return self.get_queryset().filter(~Q(username="admin")) |
@ -0,0 +1,214 @@ |
|||
# Generated by Django 5.0.2 on 2024-02-06 18:32 |
|||
|
|||
import django.contrib.auth.validators |
|||
import django.db.models.deletion |
|||
import django.utils.timezone |
|||
import form_user.apps.contas.managers |
|||
import uuid |
|||
from django.conf import settings |
|||
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="TagUnidade", |
|||
fields=[ |
|||
( |
|||
"id", |
|||
models.BigAutoField( |
|||
auto_created=True, |
|||
primary_key=True, |
|||
serialize=False, |
|||
verbose_name="ID", |
|||
), |
|||
), |
|||
("nome", models.CharField(max_length=50, verbose_name="Nome")), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name="Unidade", |
|||
fields=[ |
|||
( |
|||
"id", |
|||
models.BigAutoField( |
|||
auto_created=True, |
|||
primary_key=True, |
|||
serialize=False, |
|||
verbose_name="ID", |
|||
), |
|||
), |
|||
("nome", models.CharField(max_length=50, verbose_name="Nome")), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name="CustomUser", |
|||
fields=[ |
|||
("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", |
|||
), |
|||
), |
|||
( |
|||
"username", |
|||
models.CharField( |
|||
error_messages={ |
|||
"unique": "A user with that username already exists." |
|||
}, |
|||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", |
|||
max_length=150, |
|||
unique=True, |
|||
validators=[ |
|||
django.contrib.auth.validators.UnicodeUsernameValidator() |
|||
], |
|||
verbose_name="username", |
|||
), |
|||
), |
|||
( |
|||
"first_name", |
|||
models.CharField( |
|||
blank=True, max_length=150, verbose_name="first name" |
|||
), |
|||
), |
|||
( |
|||
"last_name", |
|||
models.CharField( |
|||
blank=True, max_length=150, verbose_name="last name" |
|||
), |
|||
), |
|||
( |
|||
"email", |
|||
models.EmailField( |
|||
blank=True, max_length=254, verbose_name="email address" |
|||
), |
|||
), |
|||
( |
|||
"date_joined", |
|||
models.DateTimeField( |
|||
default=django.utils.timezone.now, verbose_name="date joined" |
|||
), |
|||
), |
|||
( |
|||
"id", |
|||
models.UUIDField( |
|||
default=uuid.uuid4, |
|||
editable=False, |
|||
primary_key=True, |
|||
serialize=False, |
|||
verbose_name="Identifier", |
|||
), |
|||
), |
|||
( |
|||
"is_staff", |
|||
models.BooleanField(default=False, verbose_name="staff status"), |
|||
), |
|||
( |
|||
"is_active", |
|||
models.BooleanField(default=False, verbose_name="active"), |
|||
), |
|||
( |
|||
"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", |
|||
), |
|||
), |
|||
( |
|||
"usuario_agrupador", |
|||
models.ForeignKey( |
|||
on_delete=django.db.models.deletion.CASCADE, |
|||
related_name="agrupador", |
|||
to=settings.AUTH_USER_MODEL, |
|||
verbose_name="Usuário Agrupador", |
|||
), |
|||
), |
|||
], |
|||
options={ |
|||
"verbose_name": "Usuário", |
|||
"verbose_name_plural": "Usuários", |
|||
}, |
|||
managers=[ |
|||
("objects", form_user.apps.contas.managers.CustomUserManager()), |
|||
], |
|||
), |
|||
migrations.CreateModel( |
|||
name="UsuarioPermissaoUnidadeTag", |
|||
fields=[ |
|||
( |
|||
"id", |
|||
models.BigAutoField( |
|||
auto_created=True, |
|||
primary_key=True, |
|||
serialize=False, |
|||
verbose_name="ID", |
|||
), |
|||
), |
|||
( |
|||
"tags", |
|||
models.ManyToManyField(to="contas.tagunidade", verbose_name="Tags"), |
|||
), |
|||
( |
|||
"unidades", |
|||
models.ManyToManyField( |
|||
to="contas.unidade", verbose_name="Unidades" |
|||
), |
|||
), |
|||
( |
|||
"usuario", |
|||
models.ForeignKey( |
|||
blank=True, |
|||
null=True, |
|||
on_delete=django.db.models.deletion.CASCADE, |
|||
related_name="per_custom_user", |
|||
to=settings.AUTH_USER_MODEL, |
|||
), |
|||
), |
|||
( |
|||
"usuario_inclusao", |
|||
models.ForeignKey( |
|||
blank=True, |
|||
null=True, |
|||
on_delete=django.db.models.deletion.CASCADE, |
|||
related_name="usuario_inclusao", |
|||
to=settings.AUTH_USER_MODEL, |
|||
), |
|||
), |
|||
], |
|||
options={ |
|||
"verbose_name": "PermissĂŁo de Acesso Tag e Unidade", |
|||
"verbose_name_plural": "Permissões de Acesso Tags e Unidades", |
|||
}, |
|||
), |
|||
] |
@ -0,0 +1,27 @@ |
|||
# Generated by Django 5.0.2 on 2024-02-06 18:35 |
|||
|
|||
import django.db.models.deletion |
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("contas", "0001_initial"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name="customuser", |
|||
name="usuario_agrupador", |
|||
field=models.ForeignKey( |
|||
blank=True, |
|||
null=True, |
|||
on_delete=django.db.models.deletion.CASCADE, |
|||
related_name="agrupador", |
|||
to=settings.AUTH_USER_MODEL, |
|||
verbose_name="Usuário Agrupador", |
|||
), |
|||
), |
|||
] |
@ -0,0 +1,79 @@ |
|||
import uuid |
|||
|
|||
from django.contrib.auth.models import AbstractUser |
|||
from django.db import models |
|||
from django.utils.translation import gettext_lazy as _ |
|||
|
|||
from .managers import CustomUserManager |
|||
|
|||
|
|||
class TagUnidade(models.Model): |
|||
nome = models.CharField("Nome", max_length=50) |
|||
|
|||
def __str__(self): |
|||
return self.nome |
|||
|
|||
|
|||
class Unidade(models.Model): |
|||
nome = models.CharField("Nome", max_length=50) |
|||
|
|||
def __str__(self): |
|||
return self.nome |
|||
|
|||
|
|||
class CustomUser(AbstractUser): |
|||
id = models.UUIDField( |
|||
_("Identifier"), primary_key=True, default=uuid.uuid4, editable=False |
|||
) |
|||
is_staff = models.BooleanField(_("staff status"), default=False) |
|||
is_active = models.BooleanField(_("active"), default=False) |
|||
|
|||
usuario_agrupador = models.ForeignKey( |
|||
"self", |
|||
on_delete=models.CASCADE, |
|||
verbose_name="Usuário Agrupador", |
|||
related_name="agrupador", |
|||
null=True, |
|||
blank=True, |
|||
) |
|||
|
|||
EMAIL_FIELD = "email" |
|||
USERNAME_FIELD = "username" |
|||
REQUIRED_FIELDS = ["email"] |
|||
|
|||
objects = CustomUserManager() |
|||
|
|||
class Meta: |
|||
verbose_name = "Usuário" |
|||
verbose_name_plural = "Usuários" |
|||
|
|||
def __str__(self): |
|||
return self.first_name or self.username |
|||
|
|||
|
|||
class UsuarioPermissaoUnidadeTag(models.Model): |
|||
usuario = models.ForeignKey( |
|||
CustomUser, |
|||
on_delete=models.CASCADE, |
|||
related_name="per_custom_user", |
|||
null=True, |
|||
blank=True, |
|||
) |
|||
|
|||
tags = models.ManyToManyField(TagUnidade, verbose_name="Tags") |
|||
unidades = models.ManyToManyField(Unidade, verbose_name="Unidades") |
|||
|
|||
usuario_inclusao = models.ForeignKey( |
|||
CustomUser, |
|||
on_delete=models.CASCADE, |
|||
related_name="usuario_inclusao", |
|||
null=True, |
|||
blank=True, |
|||
) |
|||
|
|||
def __str__(self): |
|||
return self.usuario.username |
|||
|
|||
class Meta: |
|||
verbose_name = "PermissĂŁo de Acesso Tag e Unidade" |
|||
verbose_name_plural = "Permissões de Acesso Tags e Unidades" |
@ -0,0 +1,15 @@ |
|||
*, |
|||
*::before, |
|||
*::after { |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
|
|||
body { |
|||
display: grid; |
|||
place-content: center; |
|||
align-items: center; |
|||
height: 100vh; |
|||
} |
@ -0,0 +1,13 @@ |
|||
{% load static %} |
|||
|
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<link rel="stylesheet" href="{% static 'contas/css/main.css' %}"> |
|||
<title>Form Test</title> |
|||
</head> |
|||
<body> |
|||
{% block content %}{% endblock content %} |
|||
</body> |
|||
</html> |
@ -0,0 +1,13 @@ |
|||
{% extends "contas/base.html" %} |
|||
|
|||
{% load static %} |
|||
|
|||
{% block content %} |
|||
<a href="{% url 'contas:index' %}">Voltar</a> |
|||
<form action="" method="post" style="margin-top: 2rem;" autocomplete="off"> |
|||
{% csrf_token %} |
|||
{{ form.as_p }} |
|||
{{ form_permissao.as_p }} |
|||
<input type="submit" value="Salvar"> |
|||
</form> |
|||
{% endblock content %} |
@ -0,0 +1,13 @@ |
|||
{% extends "contas/base.html" %} |
|||
|
|||
{% load static %} |
|||
|
|||
{% block content %} |
|||
<a href="{% url 'contas:novo' %}">Novo Usuário</a><br> |
|||
<ul style="margin-top: 2rem;"> |
|||
{% for object in object_list %} |
|||
<li><a href="{% url 'contas:editar' object.pk %}">{{object}}</a></li> |
|||
{% endfor %} |
|||
</ul> |
|||
{% endblock content %} |
|||
|
@ -0,0 +1,3 @@ |
|||
from django.test import TestCase |
|||
|
|||
# Create your tests here. |
@ -0,0 +1,11 @@ |
|||
from django.urls import path |
|||
|
|||
from . import views |
|||
|
|||
app_name = "contas" |
|||
|
|||
urlpatterns = [ |
|||
path("", views.Index.as_view(), name="index"), |
|||
path("novo/", views.Novo.as_view(), name="novo"), |
|||
path("editar/<uuid:pk>/", views.Editar.as_view(), name="editar"), |
|||
] |
@ -0,0 +1,55 @@ |
|||
from django.db import transaction |
|||
from django.http import HttpResponseRedirect |
|||
from django.urls import reverse_lazy |
|||
from django.views.generic import CreateView, ListView, UpdateView |
|||
|
|||
from form_user.apps.contas.models import CustomUser |
|||
|
|||
from . import forms |
|||
|
|||
|
|||
class Common: |
|||
model = CustomUser |
|||
template_name = "contas/form.html" |
|||
form_class = forms.NewUserForm |
|||
success_url = reverse_lazy("contas:index") |
|||
|
|||
|
|||
class Index(Common, ListView): |
|||
template_name = "contas/index.html" |
|||
paginate_by = 10 |
|||
|
|||
|
|||
class NovoEditar(Common): |
|||
def form_valid(self, form): |
|||
form_permissao = forms.PermissaoForm(self.request.POST) |
|||
form_permissao.full_clean() |
|||
|
|||
if not form_permissao.is_valid(): |
|||
context = self.get_context_data(form=form) |
|||
context.update(form_permissao=form_permissao) |
|||
return self.render_to_response(context) |
|||
|
|||
with transaction.atomic(): |
|||
ob = form.save() |
|||
form_permissao.instance.usuario = ob |
|||
form_permissao.instance.usuario_inclusao = ob |
|||
form_permissao.save() |
|||
|
|||
return HttpResponseRedirect(self.success_url) |
|||
|
|||
|
|||
class Novo(NovoEditar, CreateView): |
|||
def get_context_data(self, **kwargs): |
|||
context = super().get_context_data(**kwargs) |
|||
context["form_permissao"] = forms.PermissaoForm() |
|||
return context |
|||
|
|||
|
|||
class Editar(NovoEditar, UpdateView): |
|||
def get_context_data(self, **kwargs): |
|||
context = super().get_context_data(**kwargs) |
|||
context["form_permissao"] = forms.PermissaoForm( |
|||
instance=self.object.per_custom_user.first() |
|||
) |
|||
return context |
@ -0,0 +1,16 @@ |
|||
""" |
|||
ASGI config for form_user 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.0/howto/deployment/asgi/ |
|||
""" |
|||
|
|||
import os |
|||
|
|||
from django.core.asgi import get_asgi_application |
|||
|
|||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'form_user.settings') |
|||
|
|||
application = get_asgi_application() |
@ -0,0 +1,138 @@ |
|||
""" |
|||
Django settings for form_user project. |
|||
|
|||
Generated by 'django-admin startproject' using Django 5.0.2. |
|||
|
|||
For more information on this file, see |
|||
https://docs.djangoproject.com/en/5.0/topics/settings/ |
|||
|
|||
For the full list of settings and their values, see |
|||
https://docs.djangoproject.com/en/5.0/ref/settings/ |
|||
""" |
|||
|
|||
import os |
|||
from pathlib import Path |
|||
|
|||
from decouple import Csv, config |
|||
from dj_database_url import parse as dburl |
|||
|
|||
# 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.0/howto/deployment/checklist/ |
|||
|
|||
# SECURITY WARNING: keep the secret key used in production secret! |
|||
SECRET_KEY = config("SECRET_KEY") |
|||
|
|||
# SECURITY WARNING: don't run with debug turned on in production! |
|||
DEBUG = config("DEBUG", default=False, cast=bool) |
|||
|
|||
ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=Csv()) |
|||
|
|||
# Application definition |
|||
|
|||
INSTALLED_APPS = [ |
|||
"django.contrib.admin", |
|||
"django.contrib.auth", |
|||
"django.contrib.contenttypes", |
|||
"django.contrib.sessions", |
|||
"django.contrib.messages", |
|||
"django.contrib.staticfiles", |
|||
"django_extensions", |
|||
"form_user.apps.contas", |
|||
] |
|||
|
|||
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 = "form_user.urls" |
|||
|
|||
TEMPLATES = [ |
|||
{ |
|||
"BACKEND": "django.template.backends.django.DjangoTemplates", |
|||
"DIRS": ["templates", BASE_DIR / "templates"], |
|||
"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 = "form_user.wsgi.application" |
|||
|
|||
|
|||
# Database |
|||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases |
|||
|
|||
default_dburl = "sqlite:///" + os.path.join(BASE_DIR, "db.sqlite3") |
|||
|
|||
DATABASES = { |
|||
"default": config("DATABASE_URL", default=default_dburl, cast=dburl), |
|||
} |
|||
|
|||
|
|||
# Password validation |
|||
# https://docs.djangoproject.com/en/5.0/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.0/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.0/howto/static-files/ |
|||
STATIC_URL = "static/" |
|||
|
|||
STATICFILES_DIRS = [ |
|||
os.path.join(BASE_DIR, "staticfiles"), |
|||
] |
|||
|
|||
MEDIA_URL = "/media/" |
|||
|
|||
MEDIA_ROOT = os.path.join(BASE_DIR, "media") |
|||
|
|||
STATIC_ROOT = os.path.join(BASE_DIR, "static/") |
|||
|
|||
# Default primary key field type |
|||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field |
|||
|
|||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" |
|||
|
|||
AUTH_USER_MODEL = "contas.CustomUser" |
@ -0,0 +1,24 @@ |
|||
""" |
|||
URL configuration for form_user project. |
|||
|
|||
The `urlpatterns` list routes URLs to views. For more information please see: |
|||
https://docs.djangoproject.com/en/5.0/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 include, path |
|||
|
|||
urlpatterns = [ |
|||
path("admin/", admin.site.urls), |
|||
path("", include("form_user.apps.contas.urls", namespace="contas")), |
|||
] |
@ -0,0 +1,16 @@ |
|||
""" |
|||
WSGI config for form_user 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.0/howto/deployment/wsgi/ |
|||
""" |
|||
|
|||
import os |
|||
|
|||
from django.core.wsgi import get_wsgi_application |
|||
|
|||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'form_user.settings') |
|||
|
|||
application = get_wsgi_application() |
@ -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', 'form_user.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() |
@ -0,0 +1,118 @@ |
|||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. |
|||
|
|||
[[package]] |
|||
name = "asgiref" |
|||
version = "3.7.2" |
|||
description = "ASGI specs, helper code, and adapters" |
|||
optional = false |
|||
python-versions = ">=3.7" |
|||
files = [ |
|||
{file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, |
|||
{file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] |
|||
|
|||
[[package]] |
|||
name = "dj-database-url" |
|||
version = "2.1.0" |
|||
description = "Use Database URLs in your Django Application." |
|||
optional = false |
|||
python-versions = "*" |
|||
files = [ |
|||
{file = "dj-database-url-2.1.0.tar.gz", hash = "sha256:f2042cefe1086e539c9da39fad5ad7f61173bf79665e69bf7e4de55fa88b135f"}, |
|||
{file = "dj_database_url-2.1.0-py3-none-any.whl", hash = "sha256:04bc34b248d4c21aaa13e4ab419ae6575ef5f10f3df735ce7da97722caa356e0"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
Django = ">=3.2" |
|||
typing-extensions = ">=3.10.0.0" |
|||
|
|||
[[package]] |
|||
name = "django" |
|||
version = "5.0.2" |
|||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." |
|||
optional = false |
|||
python-versions = ">=3.10" |
|||
files = [ |
|||
{file = "Django-5.0.2-py3-none-any.whl", hash = "sha256:56ab63a105e8bb06ee67381d7b65fe6774f057e41a8bab06c8020c8882d8ecd4"}, |
|||
{file = "Django-5.0.2.tar.gz", hash = "sha256:b5bb1d11b2518a5f91372a282f24662f58f66749666b0a286ab057029f728080"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
asgiref = ">=3.7.0,<4" |
|||
sqlparse = ">=0.3.1" |
|||
tzdata = {version = "*", markers = "sys_platform == \"win32\""} |
|||
|
|||
[package.extras] |
|||
argon2 = ["argon2-cffi (>=19.1.0)"] |
|||
bcrypt = ["bcrypt"] |
|||
|
|||
[[package]] |
|||
name = "django-extensions" |
|||
version = "3.2.3" |
|||
description = "Extensions for Django" |
|||
optional = false |
|||
python-versions = ">=3.6" |
|||
files = [ |
|||
{file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, |
|||
{file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, |
|||
] |
|||
|
|||
[package.dependencies] |
|||
Django = ">=3.2" |
|||
|
|||
[[package]] |
|||
name = "python-decouple" |
|||
version = "3.8" |
|||
description = "Strict separation of settings from code." |
|||
optional = false |
|||
python-versions = "*" |
|||
files = [ |
|||
{file = "python-decouple-3.8.tar.gz", hash = "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f"}, |
|||
{file = "python_decouple-3.8-py3-none-any.whl", hash = "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "sqlparse" |
|||
version = "0.4.4" |
|||
description = "A non-validating SQL parser." |
|||
optional = false |
|||
python-versions = ">=3.5" |
|||
files = [ |
|||
{file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, |
|||
{file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, |
|||
] |
|||
|
|||
[package.extras] |
|||
dev = ["build", "flake8"] |
|||
doc = ["sphinx"] |
|||
test = ["pytest", "pytest-cov"] |
|||
|
|||
[[package]] |
|||
name = "typing-extensions" |
|||
version = "4.9.0" |
|||
description = "Backported and Experimental Type Hints for Python 3.8+" |
|||
optional = false |
|||
python-versions = ">=3.8" |
|||
files = [ |
|||
{file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, |
|||
{file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, |
|||
] |
|||
|
|||
[[package]] |
|||
name = "tzdata" |
|||
version = "2023.4" |
|||
description = "Provider of IANA time zone data" |
|||
optional = false |
|||
python-versions = ">=2" |
|||
files = [ |
|||
{file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, |
|||
{file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, |
|||
] |
|||
|
|||
[metadata] |
|||
lock-version = "2.0" |
|||
python-versions = "^3.11" |
|||
content-hash = "3f3d4eb61266813806f194403d47bbd1801f62ca247a7ef3dc226a606fe9327f" |
@ -0,0 +1,17 @@ |
|||
[tool.poetry] |
|||
name = "form-user" |
|||
version = "0.1.0" |
|||
description = "" |
|||
authors = ["Lucas F. <lucas@lucasf.dev>"] |
|||
readme = "README.md" |
|||
|
|||
[tool.poetry.dependencies] |
|||
python = "^3.11" |
|||
django = "^5.0.2" |
|||
python-decouple = "^3.8" |
|||
django-extensions = "^3.2.3" |
|||
dj-database-url = "^2.1.0" |
|||
|
|||
[build-system] |
|||
requires = ["poetry-core"] |
|||
build-backend = "poetry.core.masonry.api" |
@ -0,0 +1,8 @@ |
|||
asgiref==3.7.2 ; python_version >= "3.11" and python_version < "4.0" |
|||
dj-database-url==2.1.0 ; python_version >= "3.11" and python_version < "4.0" |
|||
django-extensions==3.2.3 ; python_version >= "3.11" and python_version < "4.0" |
|||
django==5.0.2 ; python_version >= "3.11" and python_version < "4.0" |
|||
python-decouple==3.8 ; python_version >= "3.11" and python_version < "4.0" |
|||
sqlparse==0.4.4 ; python_version >= "3.11" and python_version < "4.0" |
|||
typing-extensions==4.9.0 ; python_version >= "3.11" and python_version < "4.0" |
|||
tzdata==2023.4 ; python_version >= "3.11" and python_version < "4.0" and sys_platform == "win32" |
Loading…
Reference in new issue