Aula 81 - Loja Online - Gerenciador do Modelo User
Como vimos na
aula 79, o modelo de usuário padrão é definido no módulo
django.contrib.auth.models e inclui campos comuns como nome de usuário, email e senha.
Além do modelo de usuário, o
Django também inclui um gerenciador de modelo de usuário padrão, que é responsável por criar, atualizar, excluir e pesquisar objetos de usuário no banco de dados.
O gerenciador de modelo de usuário padrão é definido no modelo de usuário padrão do Django e é acessível por meio do objeto
User.objects em seus pontos de código.
No entanto, se você criar seu próprio modelo de usuário personalizado, precisará criar um gerenciador de modelo de usuário personalizado também, que será responsável por gerenciar objetos do seu modelo de usuário personalizado no banco de dados.
O gerenciador de modelo de usuário personalizado deve herdar da classe
BaseUserManager do
Django e substituir os métodos relevantes para
criar,
atualizar,
excluir e
pesquisar objetos do seu modelo de usuário personalizado.
Crie a branch para trabalhar no código da aula, no meu caso, vou chamar de
user-model-manager.
git checkout -b user-model-manager
django_ecommerce/e_commerce/accounts/models.py
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUserManager):
def get_by_natural_key(self, email):
return self.get(email=email)
def create_user(self, email, password = None, is_active = True, is_staff = False, is_admin = False):
if not email:
raise ValueError("O Usuário deve ter um endereço de email.")
if not password:
raise ValueError("O Usuário deve ter uma senha.")
user_obj = self.model(
email = self.normalize_email(email)
)
user_obj.set_password(password) # muda a senha
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_staffuser(self, email, password = None):
user = self.create_user(
email,
password = password,
staff = True
)
return user
def create_superuser(self, email, password = None):
user = self.create_user(
email,
password = password,
is_staff = True,
is_admin = True,
)
return user
class User(AbstractBaseUser):
#full_name = models.CharField(max_length=255, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
active = models.BooleanField(default=True) # can do login
staff = models.BooleanField(default=False) # staff user, non superuser
admin = models.BooleanField(default=False) #superuser
timestamp = models.DateTimeField(auto_now_add=True)
# confirm = models.BooleanField(default=False)
# confirmed_date = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
# USERNAME_FIELD and password are required by default
REQUIRED_FIELDS = [] # ['full_name'] #python manage.py createsuperuser
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
@property
def is_staff(self):
return self.staff
@property
def is_admin(self):
return self.admin
@property
def is_active(self):
return self.active
class GuestEmail(models.Model):
email = models.EmailField()
active = models.BooleanField(default=True)
update = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
Faça a
migrations com:
python manage.py makemigrations
Provavelmente, será solicitado o
timestamp, escolha a opção
1) Provide a one-off..., digitando
1.
No
prompt python, que vai entrar automaticamente, digite:
timezone.now e dê
enter.
Agora as
migrations foram
efetivadas.
Faltou só o
migrate:
python manage.py migrate
django_ecommerce/e_commerce/e_commerce/settings.py
"""
Django settings for e_commerce project.
Generated by 'django-admin startproject' using Django 2.1.4.
For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'xjmv-0^l__duq4-xp54m94bsf02lx4&1xka_ykd_(7(5#9^1o^'
# 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',
#our apps
'addresses',
'billing',
'accounts',
'carts',
'orders',
'products',
'search',
'tags',
]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = 'accounts.User' # changes the built-in user model to ours
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',
]
LOGOUT_REDIRECT_URL = '/login/'
ROOT_URLCONF = 'e_commerce.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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 = 'e_commerce.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.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/2.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static_local")
]
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_cdn", "static_root")
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_cdn", "media_root")
Rode o servidor:
python manage.py runserver
Veja que no
127.0.0.1:8000/admin/login, agora mostra o email como a única opção de login.
Fizemos mudanças na estrutura do banco, na tabela de
user que está ligada a
billing.
Por isso, vamos ter que criar não só um novo superuser com:
python manage.py createsuperuser
Vai dá um erro de
UNIQUE constraint failed, por causa do
billing que está
ligado ao
id de um
usuário.
Solução
Podemos deletar todos os
billing profiles.
Mas, essa é uma boa oportunidade para destruir o banco todo, mas mantendo parte dele, no nosso caso, os produtos, porque o resto dos dados são mais fáceis e rápidos de serem criados.
O que vamos fazer então é o seguinte: para não ter que recadastrar os produtos que a gente já tem no banco, vamos salvar ele para depois recuperar ele no banco novo.
Para isso, vamos usar um recurso chamado
fixture.
Na pasta do app products:
django_ecommerce/e_commerce/products
Crie uma pasta chamada:
fixtures
django_ecommerce/e_commerce/products/fixtures
Com o comando abaixo, criamos o arquivo
products.json dentro da pasta
fixtures.
python manage.py dumpdata --format json --indent 4 > products/fixtures/products.json
Podemos renomear o nosso banco sqlite:
db.sqlite3 para
db2.sqlite3.
Delete todas as
migrations e o
__pycache__, deixe so o
__init__.py no app
django_ecommerce/e_commerce/accounts/migrations.
Migrations
python manage.py makemigrations
Migrate
python manage.py migrate
Carregando os dados dos produtos
python manage.py loaddata products/fixtures/products.json
Criando o super user
python manage.py createsuperuser
Caso você esqueça da senha, para mudar ela use:
python manage.py changepassword <user_name>
Seguindo a criação do super user
Defina um
email e
senha.
Se tudo correu bem, dessa vez não houve problema na criação do super user.
Suba o servidor
python manage.py runserver
Faça o login com o email e senha que você cadastrou:
127.0.0.1:8000/admin/login
Provavelmente vai dá um erro quando fizer o login:
'User' object has no attribute 'has_module_perms'
Esse é um método que o Custom User Model Django espera que se tenha implementado.
Para solucionar, vanos criar.
django_ecommerce/e_commerce/accounts/models.py
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUSerManager):
def create_user(self, email, password = None, is_active = True, is_staff = False, is_admin = False):
if not email:
raise ValueError("O Usuário deve ter um endereço de email.")
if not password:
raise ValueError("O Usuário deve ter uma senha.")
user_obj = self.model(
email = self.normalize_email(email)
)
user_obj.set_password(password) # muda a senha
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_staffuser(self, email, password = None):
user = self.create_user(
email,
password = password,
staff = True
)
return user
def create_superuser(self, email, password = None):
user = self.create_user(
email,
password = password,
is_staff = True,
is_admin = True,
)
return user
class User(AbstractBaseUser):
#full_name = models.CharField(max_length=255, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
active = models.BooleanField(default=True) # can do login
staff = models.BooleanField(default=False) # staff user, non superuser
admin = models.BooleanField(default=False) #superuser
timestamp = models.DateTimeField(auto_now_add=True)
# confirm = models.BooleanField(default=False)
# confirmed_date = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
# USERNAME_FIELD and password are required by default
REQUIRED_FIELDS = [] # ['full_name'] #python manage.py createsuperuser
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def has_perms(self, perm, object=None):
return True
def has_module_perms(self, app_label):
return True
@property
def is_staff(self):
return self.staff
@property
def is_admin(self):
return self.admin
@property
def is_active(self):
return self.active
class GuestEmail(models.Model):
email = models.EmailField()
active = models.BooleanField(default=True)
update = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
Faça o login:
127.0.0.1:8000/admin/login
Veja que o User não aparece no admin, só o accounts.
Abra o
django_ecommerce/e_commerce/accounts/admin.py
django_ecommerce/e_commerce/accounts/admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from .models import GuestEmail
User = get_user_model()
admin.site.register(User)
admin.site.register(GuestEmail)
# Register your models here.
Salve, dê um refresh na página e veja que agora aparece o
User no painel
admin.
Na próxima aula vamos melhorar o formulários para nosso
User personalizado.
É isso, até a próxima. ;)
Código final da aula:
Canais do Youtube
Dêem um joinha 👍 na página do Código Fluente no
Facebook.
Sigam o Código Fluente no Instagram e no TikTok.
Código Fluente no Pinterest.
Meus links de afiliados:
Nos vemos na próxima então, \o/ 😉 Bons Estudos!