-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e3a9570
Showing
76 changed files
with
2,785 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Pre-commit | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
pre-commit: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: ["3.11"] | ||
|
||
steps: | ||
- name: Checkout source | ||
uses: actions/checkout@v3 | ||
|
||
## Setup Python, dependencies and migrations, pre-commit needs them | ||
- name: Setup Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -r requirements.txt | ||
- name: Run migrations | ||
run: | | ||
python manage.py makemigrations | ||
python manage.py migrate | ||
- name: Check linter | ||
run: | | ||
black --check . | ||
- name: Check coverage | ||
run: | | ||
if [ ! -e coverage/.coverage ] || [ ! -e coverage/coverage.svg ]; then | ||
echo "Have you run pre-commit?" | ||
exit 1 | ||
else | ||
echo "coverage found" | ||
fi | ||
coverage run --data-file=coverage/.coverage-CI manage.py test | ||
coverage report > coverage.txt | ||
coverage report --data-file=coverage/.coverage-CI > coverage-CI.txt | ||
diff coverage.txt coverage-CI.txt | ||
coverage-badge -f -o coverage/coverage-CI.svg | ||
diff coverage/coverage.svg coverage/coverage-CI.svg | ||
echo "Pre-commit correct" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: Tests | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: ["3.11", "3.12"] | ||
|
||
steps: | ||
- name: Checkout source | ||
uses: actions/checkout@v3 | ||
|
||
- name: Setup Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -r requirements.txt | ||
- name: Run migrations ## Migraciones para tener la BBDD como necesita la app. | ||
run: | | ||
python manage.py makemigrations | ||
python manage.py migrate | ||
- name: Run tests | ||
run: python manage.py test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.DS_Store | ||
__pycache__ | ||
.env | ||
*/migrations/* | ||
!*/migrations/__init__.py | ||
media | ||
db.sqlite3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
repos: | ||
- repo: local | ||
hooks: | ||
- id: linter | ||
name: linter | ||
entry: | | ||
black . | ||
language: system | ||
types: [python] | ||
|
||
- id: run-code-coverage | ||
name: Run Code Coverage | ||
entry: | | ||
coverage run manage.py test | ||
language: system | ||
pass_filenames: false | ||
types: [python] | ||
|
||
- id: create-coverage-badge | ||
name: Create coverage badge | ||
entry: | | ||
coverage-badge -f -o coverage/coverage.svg | ||
language: system | ||
pass_filenames: false | ||
types: [python] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright © 2024 <Tomás Senovilla Polo> | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
![CI](https://github.com/tsenovilla/django_extended_accounts/actions/workflows/tests.yaml/badge.svg) | ||
![pre-commit](https://github.com/tsenovilla/django_extended_accounts/actions/workflows/pre-commit.yaml/badge.svg) | ||
![Coverage](./coverage/coverage.svg) | ||
![Black](https://img.shields.io/badge/code%20style-black-000000.svg) | ||
|
||
# Description 📖📚 | ||
|
||
**django_extended_accounts** is a Django app designed to extend the Django's default authentication system. As this is a common task, this project aims to be a reusable template to help developers saving a lot of time. | ||
|
||
This solution is based on getting rid of the Django's User model to use a custom model called AccountModel. This model is designed to only contain authentication-related information, while all personal data resides in another model called ProfileModel, which maintains a one-to-one relationship with AccountModel. The AccountModel provided is similar to the default User model in Django, with some differences: | ||
|
||
- It does not include first and last names; these are stored in ProfileModel. | ||
- Upon saving, it executes an additional query to save profile information in ProfileModel. | ||
- Accounts are initially deactivated, requiring users to activate them via email after creation. | ||
|
||
The decision behind this design is to keep the authentication model as simple as possible and avoid interference with other apps using this solution, allowing each app to specify its own user data requirements without conflicting assumptions. However, as this is just a template, you can change this design choice if you feel it's worthy of your project. | ||
|
||
The ProfileModel contains initially the following fields: | ||
|
||
- First name. | ||
- Last name. | ||
- Phone number. | ||
- Profile Image | ||
|
||
Since this is a template, other fields aren't included for simplicity, but as the app is fully customizable this model may be altered to fit your project's requirements. | ||
|
||
The provided app deals with some usual concepts present in many website accounts' system, such as: | ||
|
||
- Allows users to upload a profile image, which is automatically converted into WebP format for efficiency while also saving the original format. If the user updates/deletes the image or the user itself is deleted, the former is automatically removed from the server. | ||
|
||
- Sends a confirmation email to the user once it creates its account. If the account is not confirmed in an arbitrary period of time, the account is removed from the ddbb. This is achieved by integrating Celery into the project as a daemon. | ||
|
||
Feel free to add/remove any functionality needed by your project. | ||
|
||
##### Important Considerations ⚠️ ❗️ | ||
|
||
- Reusable apps generally shouldn't implement a custom user model as specified in [Django docs](https://docs.djangoproject.com/en/5.0/topics/auth/customizing/#reusable-apps-and-auth-user-model). However, this app is an exception as it specifically deals with extending the user model. | ||
- Changing the authentication model mid-project is non-trivial so this app should only be used in new projects. Check [Django docs](https://docs.djangoproject.com/en/5.0/topics/auth/customizing/#changing-to-a-custom-user-model-mid-project) for further information. | ||
- Usually, you won't interact with the ProfileModel directly but through the AccountModel. AccountModel comes with two safe methods to which you can happily pass any argument related to ProfileModel. | ||
These methods will automatically handle that data for you. In addition, these methods ensure the integrity of data if something goes wrong, as they rollback the database if saving to AccountModel or ProfileModel fails. | ||
Using other methods such as ``save`` or ``create`` may cause undesired behavior, so use them only if you're perfectly fine with them. | ||
The safe methods are: | ||
|
||
- `create_user`: Use this method through the model manager to create a new account. Ex: ``Account.objects.create_user(username='johndoe', password='johndoe', email='[email protected]', phone_number=123456789)`` | ||
|
||
- `update`: Use this method directly on the model instance to update an account. Ex: ``account.update(username='johndoe', phone_number=123456789, first_name='John')`` | ||
|
||
|
||
## DRF Version 📱💡 | ||
|
||
If your project uses Django Rest Framework to construct an API, the [DRF version](https://github.com/tsenovilla/django_extended_accounts_api) may be more suitable for your needs. | ||
|
||
|
||
## Usage 📌🖇️ | ||
|
||
This application serves as a template for extending the Django user model. It is meant to be customized, extended, or reduced according to project requirements. Since it is not published on PyPI, the code is open for editing as needed in your project. Therefore, to use it you can follow this general guidelines: | ||
|
||
1. Copy the application into your Django project. | ||
2. Add specific configurations detailed at the end of `django_extended_accounts/settings.py` to your project's settings. | ||
3. Add URLs as specified in `django_extended_accounts/urls.py`. | ||
4. If using Celery, add `django_extended_accounts/celery.py` to the project's main folder (where `settings.py` resides). | ||
|
||
For the sake of simplicity, this project uses development configurations in some tasks such as image uploading or email sending. For production projects, configurations should be adapted. | ||
|
||
## Note on Celery Integration 🤝 | ||
|
||
Refer to the Celery documentation ([Celery Documentation](https://docs.celeryq.dev/en/stable/userguide/configuration.html)) for comprehensive configuration details. The included configuration is minimal for simplicity. | ||
|
||
If you feel that your project doesn't need Celery, you can happily remove it from the template following the next steps: | ||
|
||
- Remove Celery from the project's requirements. | ||
- Delete Celery configurations in `django_extended_accounts/settings.py`. | ||
- Remove `django_extended_accounts/celery.py`, `extended_accounts/helpers/tasks.py`, and `extended_accounts/signals/post_save_account_model.py`. | ||
|
||
## Contributing 📝 | ||
|
||
Any contribution is more than welcome! 😸🤝🦾 | ||
|
||
We use pre-commit to ensure code formatting with Black and to run code coverage, CI checks that this's been correctly done :). Please, adhere to these standards if you want to contribute. | ||
|
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
ASGI config for django_extended_accounts 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", "django_extended_accounts.settings") | ||
|
||
application = get_asgi_application() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import os | ||
from celery import Celery | ||
|
||
# Set the default configuration file | ||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_extended_accounts.settings") | ||
|
||
celery_app = Celery( | ||
"django_extended_accounts" | ||
) ## Rename the app with your project's name :) | ||
|
||
# Using a string here means the worker doesn't have to serialize | ||
# the configuration object to child processes. | ||
# - namespace='CELERY' means that all celery-related configuration keys | ||
# should have a prefix `CELERY_`. | ||
celery_app.config_from_object("django.conf:settings", namespace="CELERY") | ||
|
||
# Import celery task modules registered in all Django apps | ||
celery_app.autodiscover_tasks() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
from django.urls import reverse_lazy | ||
from pathlib import Path | ||
import os | ||
import sys | ||
|
||
BASE_DIR = Path(__file__).resolve().parent.parent | ||
|
||
SECRET_KEY = "django-insecure-5_z8k5snwu(im+iuha=@fo=p&bvp$#+_zilfdaozn2ugnm3g04" | ||
|
||
DEBUG = True | ||
|
||
ALLOWED_HOSTS = [] | ||
|
||
|
||
INSTALLED_APPS = [ | ||
"django.contrib.admin", | ||
"django.contrib.auth", | ||
"django.contrib.contenttypes", | ||
"django.contrib.sessions", | ||
"django.contrib.messages", | ||
"django.contrib.staticfiles", | ||
] | ||
|
||
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_extended_accounts.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 = "django_extended_accounts.wsgi.application" | ||
|
||
DATABASES = { | ||
"default": { | ||
"ENGINE": "django.db.backends.sqlite3", | ||
"NAME": BASE_DIR / "db.sqlite3", | ||
} | ||
} | ||
|
||
|
||
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", | ||
}, | ||
] | ||
|
||
LANGUAGE_CODE = "en-us" | ||
TIME_ZONE = "UTC" | ||
USE_I18N = True | ||
USE_TZ = True | ||
|
||
|
||
STATIC_URL = "static/" | ||
STATIC_ROOT = os.path.join(BASE_DIR, STATIC_URL) | ||
MEDIA_URL = "media/" | ||
MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL) | ||
|
||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" | ||
|
||
## Settings related to the django_extended_account apps start here! | ||
## Make sure to add them into your settings.py | ||
## The settings are related to a dev environment, make sure to adapt accordingly with your needs | ||
|
||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" | ||
DEFAULT_FROM_EMAIL = "[email protected]" | ||
TESTING = "test" in sys.argv | ||
INTEGRATION_TEST_CELERY = False | ||
|
||
## Celery settings. | ||
## Here the configuration is minimal, refer to the official docs https://docs.celeryq.dev/en/stable/userguide/configuration.html to check out all the availables options. If you're not using Celery in your project, you can happily delete them. | ||
CELERY_BROKER_URL = "pyamqp://" | ||
|
||
|
||
## extended_accounts app | ||
|
||
AUTH_USER_MODEL = "extended_accounts.AccountModel" | ||
INSTALLED_APPS += ["extended_accounts"] | ||
LOGIN_URL = reverse_lazy("extended_accounts:login") | ||
LOGIN_REDIRECT_URL = reverse_lazy("extended_accounts:redirect_account") | ||
LOGOUT_REDIRECT_URL = reverse_lazy("extended_accounts:login") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from django.urls import path, include | ||
from django.conf import settings | ||
|
||
urlpatterns = [] | ||
|
||
## If you are using the extended_accounts app | ||
urlpatterns += [ | ||
path( | ||
"extended_accounts/", | ||
include("extended_accounts.urls", namespace="extended_accounts"), | ||
) | ||
] | ||
## Use this setting to correctly visualize your images in development django templates. | ||
if settings.DEBUG: | ||
from django.conf.urls.static import static | ||
|
||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
Oops, something went wrong.