diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bc4a36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# OSX # +.DS_Store + +# Byte-compiled / optimized / DLL files # +__pycache__/ +*.py[cod] +*$py.class + +# Django # +*.log +db.sqlite3 +media + +# Virtual environment +.venv diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1873eaf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +Thank you for help improving DjangoX. All kinds of contributions are welcome. Please note that this starter project is *intentionally* basic: I don't plan to add environment variables, Docker, or other production-appropriate features as I feel they will overwhelm beginners. But I'm open to suggestions! + +Please submit an Issue or even better a PR and I'll review :) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f685a07 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# Pull base image +FROM python:3.12.2-slim-bookworm + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# Create and set work directory called `app` +RUN mkdir -p /code +WORKDIR /code + +# Install dependencies +COPY requirements.txt /tmp/requirements.txt + +RUN set -ex && \ + pip install --upgrade pip && \ + pip install -r /tmp/requirements.txt && \ + rm -rf /root/.cache/ + +# Copy local project +COPY . /code/ + +# Expose port 8000 +EXPOSE 8000 + +# Use gunicorn on port 8000 +CMD ["gunicorn", "--bind", ":8000", "--workers", "2", "django_project.wsgi"] \ No newline at end of file diff --git a/LICENSE b/LICENSE index 08307e0..1e57ae8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -djangox: Copyright (c) 2018 William Vincent +djangox: Copyright (c) 2020 William Vincent django-allauth: Copyright (c) 2010 Raymond Penners and contributors -cookie-cutter-django: Copyright (c) 2013-2018 Daniel Greenfeld +cookie-cutter-django: Copyright (c) 2013-2020 Daniel Greenfeld Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -21,4 +21,4 @@ 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. \ No newline at end of file +OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 684da9d..0000000 --- a/Pipfile +++ /dev/null @@ -1,16 +0,0 @@ -[[source]] - -url = "https://pypi.python.org/simple" -verify_ssl = true -name = "pypi" - - -[packages] - -django = "*" -django-allauth = "*" -django-crispy-forms = "*" - - -[dev-packages] - diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 7301037..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,128 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "6d92f3e54c0f137198dc7ff38d1703a2c25f77a1870919b2cd22333a8fe35d51" - }, - "host-environment-markers": { - "implementation_name": "cpython", - "implementation_version": "3.6.4", - "os_name": "posix", - "platform_machine": "x86_64", - "platform_python_implementation": "CPython", - "platform_release": "17.4.0", - "platform_system": "Darwin", - "platform_version": "Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64", - "python_full_version": "3.6.4", - "python_version": "3.6", - "sys_platform": "darwin" - }, - "pipfile-spec": 6, - "requires": {}, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "certifi": { - "hashes": [ - "sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296", - "sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d" - ], - "version": "==2018.1.18" - }, - "chardet": { - "hashes": [ - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" - ], - "version": "==3.0.4" - }, - "defusedxml": { - "hashes": [ - "sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20", - "sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4" - ], - "version": "==0.5.0" - }, - "django": { - "hashes": [ - "sha256:7c8ff92285406fb349e765e9ade685eec7271d6f5c3f918e495a74768b765c99", - "sha256:dc3b61d054f1bced64628c62025d480f655303aea9f408e5996c339a543b45f0" - ], - "version": "==2.0.2" - }, - "django-allauth": { - "hashes": [ - "sha256:7b31526cccd1c46f9f09acf0703068e8a9669337d29eb065f7e8143c2d897339" - ], - "version": "==0.35.0" - }, - "django-crispy-forms": { - "hashes": [ - "sha256:d37fe72eb550b41ba651c06293fb861d5a9e6e3ada23304718858cd6250d258d", - "sha256:b29a9a671194e3a482891f319f69da81e30ae81c075f6e37adb14a83ba2c409b" - ], - "version": "==1.7.0" - }, - "idna": { - "hashes": [ - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" - ], - "version": "==2.6" - }, - "oauthlib": { - "hashes": [ - "sha256:ce57b501e906ff4f614e71c36a3ab9eacbb96d35c24d1970d2539bbc3ec70ce1" - ], - "version": "==2.0.6" - }, - "python3-openid": { - "hashes": [ - "sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", - "sha256:628d365d687e12da12d02c6691170f4451db28d6d68d050007e4a40065868502" - ], - "version": "==3.1.0" - }, - "pytz": { - "hashes": [ - "sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe", - "sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda", - "sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9", - "sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f", - "sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd", - "sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5", - "sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d", - "sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef", - "sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0" - ], - "version": "==2018.3" - }, - "requests": { - "hashes": [ - "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", - "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" - ], - "version": "==2.18.4" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:50a8ae2ce8273e384895972b56193c7409601a66d4975774c60c2aed869639ca", - "sha256:883ac416757eada6d3d07054ec7092ac21c7f35cb1d2cf82faf205637081f468" - ], - "version": "==0.8.0" - }, - "urllib3": { - "hashes": [ - "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", - "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" - ], - "version": "==1.22" - } - }, - "develop": {} -} diff --git a/README.md b/README.md index e37155c..88943a0 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,122 @@ -# DjangoX +# Lithium: A Django-Powered Boilerplate +Lithium is a batteries-included Django starter project with everything you need to start coding, including user authentication, static files, default styling, debugging, DRY forms, custom error pages, and more. -**DjangoX** - A framework for launching new Django projects quickly. +> This project was formerly known as _DjangoX_ but was renamed to _Lithium_ in November 2024. -Comes with a custom user model, social authentication, and email/password for sign up and log in. +https://github.com/wsvincent/djangox/assets/766418/a73ea730-a7b4-4e53-bf51-aa68f6816d6a -![Falconx](static/images/falconx.png) +## 👋 Free Newsletter +[Sign up for updates](https://buttondown.com/lithiumsaas) to the free and upcoming premium SaaS version! -## Features +## 🚀 Features -* Django 2.0 and Python 3.6 -* [Pipenv](https://github.com/pypa/pipenv) for virtualenvs -* User registration via [django-allauth](https://github.com/pennersr/django-allauth) -* Add social auth via Google, Facebook, etc -* [Bootstrap v4](https://getbootstrap.com/) -* Custom user model with email and no username +- Django 5.1 & Python 3.12 +- Installation via [Pip](https://pypi.org/project/pip/) or [Docker](https://www.docker.com/) +- User authentication--log in, sign up, password reset--via [django-allauth](https://github.com/pennersr/django-allauth) +- Static files configured with [Whitenoise](http://whitenoise.evans.io/en/stable/index.html) +- Styling with [Bootstrap v5](https://getbootstrap.com/) +- Debugging with [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) +- DRY forms with [django-crispy-forms](https://github.com/django-crispy-forms/django-crispy-forms) +- Custom 404, 500, and 403 error pages -## First-time setup +## Table of Contents +* **[Installation](#installation)** + * [Pip](#pip) + * [Docker](#docker) +* [Next Steps](#next-steps) +* [Contributing](#contributing) +* [Support](#support) +* [License](#license) -1. Make sure Python 3.6x and Pipenv are already installed. [See here for help](https://djangoforbeginners.com/initial-setup/). -2. Install packages with `pipenv install` -3. Activate a virtual environment with `pipenv shell` -4. Set up the initial migration for our custom user models in `users` +## 📖 Installation +Lithium can be installed via Pip or Docker. To start, clone the repo to your local computer and change into the proper directory. - $ python manage.py makemigrations users +``` +$ git clone https://github.com/wsvincent/lithium.git +$ cd lithium +``` -5. Build the database schema: +### Pip +You can use [pip](https://pypi.org/project/pip/) to create a fresh virtual environment on either Windows or macOS. - $ python manage.py migrate +``` +# On Windows +$ python -m venv .venv +$ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +$ .venv\Scripts\Activate.ps1 +(.venv) $ -6. Create a superuser: +# On macOS +$ python -m venv .venv +$ source .venv/bin/activate +(.venv) $ +``` - $ python manage.py createsuperuser +Then install all packages hosted in `requirements.txt` and run `migrate` to configure the initial database. The command `createsuperuser` will create a new superuser account for accessing the admin. Execute the `runserver` commandt o start up the local server. -7. Confirm everything is working: +``` +(.venv) $ pip install -r requirements.txt +(.venv) $ python manage.py migrate +(.venv) $ python manage.py createsuperuser +(.venv) $ python manage.py runserver +# Load the site at http://127.0.0.1:8000 or http://127.0.0.1:8000/admin for the admin +``` - $ python manage.py runserver +### Docker - Load the site at [http://127.0.0.1:8000](http://127.0.0.1:8000). +To use Docker with PostgreSQL as the database update the `DATABASES` section of `django_project/settings.py` to reflect the following: - Click on links for "Sign up" or "Log in." +```python +# django_project/settings.py +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "postgres", + "USER": "postgres", + "PASSWORD": "postgres", + "HOST": "db", # set in docker-compose.yml + "PORT": 5432, # default postgres port + } +} +``` -8. This is optional but I also recommend logging into admin and changing the default site: +The `INTERNAL_IPS` configuration in `django_project/settings.py` must be also be updated: - Go to [http://127.0.0.1:8000/admin]([http://127.0.0.1:8000/admin]). You may need to logout and then login with your superuser account. +```python +# config/settings.py +# django-debug-toolbar +import socket +hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) +INTERNAL_IPS = [ip[:-1] + "1" for ip in ips] +``` - Navigate to [http://127.0.0.1:8000/admin/sites/site/](http://127.0.0.1:8000/admin/sites/site/) and change the default "example.com" to "127.0.0.1" and the name to "" for local development. +And then proceed to build the Docker image, run the container, and execute the standard commands within Docker. -## Recommendations +``` +$ docker compose up -d --build +$ docker compose exec web python manage.py migrate +$ docker compose exec web python manage.py createsuperuser +# Load the site at http://127.0.0.1:8000 or http://127.0.0.1:8000/admin for the admin +``` -* Use [PostgreSQL locally via Docker](https://wsvincent.com/django-docker-postgresql/) -* Use [django-environ](https://github.com/joke2k/django-environ) for environment variables -* Update [EMAIL_BACKEND](https://docs.djangoproject.com/en/2.0/topics/email/#module-django.core.mail) to [configure an SMTP backend](https://djangoforbeginners.com/password-change-reset/) -* Add [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) and [django-extensions](https://github.com/django-extensions/django-extensions) +## Next Steps -* Make the [admin more secure](https://opensource.com/article/18/1/10-tips-making-django-admin-more-secure) +- Add environment variables. There are multiple packages but I personally prefer [environs](https://pypi.org/project/environs/). +- Add [gunicorn](https://pypi.org/project/gunicorn/) as the production web server. +- Update the [EMAIL_BACKEND](https://docs.djangoproject.com/en/4.0/topics/email/#module-django.core.mail) and connect with a mail provider. +- Make the [admin more secure](https://opensource.com/article/18/1/10-tips-making-django-admin-more-secure). +- `django-allauth` supports [social authentication](https://django-allauth.readthedocs.io/en/latest/providers.html) if you need that. -## Adding Social Authentication +I cover all of these steps in tutorials and premium courses over at [LearnDjango.com](https://learndjango.com). -* [Configuring Google](https://wsvincent.com/django-allauth-tutorial-custom-user-model/#google-credentials) -* [Configuring Facebook](http://www.sarahhagstrom.com/2013/09/the-missing-django-allauth-tutorial/#Create_and_configure_a_Facebook_app) -* [Configuring Github](https://wsvincent.com/django-allauth-tutorial/) -* `django-allauth` supports [many, many other providers in the official docs](https://django-allauth.readthedocs.io/en/latest/providers.html) +## 🤝 Contributing -## Acknowledgments +Contributions, issues and feature requests are welcome! See [CONTRIBUTING.md](https://github.com/wsvincent/djangox/blob/master/CONTRIBUTING.md). -This project is heavily inspired by [cookiecutter-django](https://github.com/pydanny/cookiecutter-django). It's my own preferred template for starting new projects built out of a personal desire to actually understand all the config magic in `cookiecutter-django`. +## ⭐️ Support + +Give a ⭐️ if this project helped you! + +## License + +[The MIT License](LICENSE) diff --git a/djangox/__init__.py b/accounts/__init__.py similarity index 100% rename from djangox/__init__.py rename to accounts/__init__.py diff --git a/users/admin.py b/accounts/admin.py similarity index 54% rename from users/admin.py rename to accounts/admin.py index 09c7b04..f3bd5c3 100644 --- a/users/admin.py +++ b/accounts/admin.py @@ -7,9 +7,24 @@ from .models import CustomUser class CustomUserAdmin(UserAdmin): - model = CustomUser add_form = CustomUserCreationForm form = CustomUserChangeForm + model = CustomUser + list_display = [ + "email", + "username", + ] + + # Explicitly define add_fieldsets to prevent unexpected fields + add_fieldsets = ( + ( + None, + { + "classes": ("wide",), + "fields": ("username", "email", "password1", "password2"), + }, + ), + ) admin.site.register(CustomUser, CustomUserAdmin) diff --git a/accounts/apps.py b/accounts/apps.py new file mode 100644 index 0000000..3e3c765 --- /dev/null +++ b/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/users/forms.py b/accounts/forms.py similarity index 79% rename from users/forms.py rename to accounts/forms.py index 2973807..4a3063b 100644 --- a/users/forms.py +++ b/accounts/forms.py @@ -2,16 +2,14 @@ from django import forms from django.contrib.auth.forms import UserCreationForm, UserChangeForm from .models import CustomUser - class CustomUserCreationForm(UserCreationForm): class Meta(UserCreationForm.Meta): model = CustomUser - fields = ('username', 'email') - + fields = ('email', 'username',) class CustomUserChangeForm(UserChangeForm): class Meta: model = CustomUser - fields = UserChangeForm.Meta.fields + fields = ('email', 'username',) \ No newline at end of file diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..516817e --- /dev/null +++ b/accounts/migrations/0001_initial.py @@ -0,0 +1,117 @@ +# Generated by Django 5.0.2 on 2024-02-19 12:10 + +import django.contrib.auth.models +import django.contrib.auth.validators +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', + ), + ), + ( + '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'), + ), + ( + 'is_staff', + models.BooleanField( + default=False, + help_text='Designates whether the user can log into this admin site.', + verbose_name='staff status', + ), + ), + ( + 'is_active', + models.BooleanField( + default=True, + help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', + verbose_name='active', + ), + ), + ( + 'date_joined', + models.DateTimeField( + default=django.utils.timezone.now, verbose_name='date joined' + ), + ), + ( + '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={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/users/__init__.py b/accounts/migrations/__init__.py similarity index 100% rename from users/__init__.py rename to accounts/migrations/__init__.py diff --git a/accounts/models.py b/accounts/models.py new file mode 100644 index 0000000..ff1ac10 --- /dev/null +++ b/accounts/models.py @@ -0,0 +1,8 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models + +class CustomUser(AbstractUser): + pass + + def __str__(self): + return self.email \ No newline at end of file diff --git a/users/tests.py b/accounts/tests.py similarity index 100% rename from users/tests.py rename to accounts/tests.py diff --git a/accounts/views.py b/accounts/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/accounts/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/django_project/__init__.py b/django_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_project/asgi.py b/django_project/asgi.py new file mode 100644 index 0000000..fc0ec52 --- /dev/null +++ b/django_project/asgi.py @@ -0,0 +1,7 @@ +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_project/settings.py b/django_project/settings.py new file mode 100644 index 0000000..063c8fd --- /dev/null +++ b/django_project/settings.py @@ -0,0 +1,202 @@ +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/dev/howto/deployment/checklist/ + +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-0peo@#x9jur3!h$ryje!$879xww8y1y66jx!%*#ymhg&jkozs2" + +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] + + +# Application definition +# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "whitenoise.runserver_nostatic", + "django.contrib.staticfiles", + "django.contrib.sites", + # Third-party + "allauth", + "allauth.account", + "crispy_forms", + "crispy_bootstrap5", + "debug_toolbar", + # Local + "accounts", + "pages", +] + +# https://docs.djangoproject.com/en/dev/ref/settings/#middleware +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", # WhiteNoise + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", # Django Debug Toolbar + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", # django-allauth +] + +# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf +ROOT_URLCONF = "django_project.urls" + +# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application +WSGI_APPLICATION = "django_project.wsgi.application" + +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [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", + ], + }, + }, +] + +# https://docs.djangoproject.com/en/dev/ref/settings/#databases +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + +# For Docker/PostgreSQL usage uncomment this and comment the DATABASES config above +# DATABASES = { +# "default": { +# "ENGINE": "django.db.backends.postgresql", +# "NAME": "postgres", +# "USER": "postgres", +# "PASSWORD": "postgres", +# "HOST": "db", # set in docker-compose.yml +# "PORT": 5432, # default postgres port +# } +# } + +# Password validation +# https://docs.djangoproject.com/en/dev/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/dev/topics/i18n/ +# https://docs.djangoproject.com/en/dev/ref/settings/#language-code +LANGUAGE_CODE = "en-us" + +# https://docs.djangoproject.com/en/dev/ref/settings/#time-zone +TIME_ZONE = "UTC" + +# https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-USE_I18N +USE_I18N = True + +# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz +USE_TZ = True + +# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths +LOCALE_PATHS = [BASE_DIR / 'locale'] + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +# https://docs.djangoproject.com/en/dev/ref/settings/#static-root +STATIC_ROOT = BASE_DIR / "staticfiles" + +# https://docs.djangoproject.com/en/dev/ref/settings/#static-url +STATIC_URL = "/static/" + +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS +STATICFILES_DIRS = [BASE_DIR / "static"] + +# https://whitenoise.readthedocs.io/en/latest/django.html +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", + }, +} + +# Default primary key field type +# https://docs.djangoproject.com/en/stable/ref/settings/#default-auto-field +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# django-crispy-forms +# https://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs +CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' +CRISPY_TEMPLATE_PACK = "bootstrap5" + +# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + +# https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email +DEFAULT_FROM_EMAIL = "root@localhost" + +# django-debug-toolbar +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html +# https://docs.djangoproject.com/en/dev/ref/settings/#internal-ips +INTERNAL_IPS = ["127.0.0.1"] + +# https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model +AUTH_USER_MODEL = "accounts.CustomUser" + +# django-allauth config +# https://docs.djangoproject.com/en/dev/ref/settings/#site-id +SITE_ID = 1 + +# https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url +LOGIN_REDIRECT_URL = "home" + +# https://django-allauth.readthedocs.io/en/latest/views.html#logout-account-logout +ACCOUNT_LOGOUT_REDIRECT_URL = "home" + +# https://django-allauth.readthedocs.io/en/latest/installation.html?highlight=backends +AUTHENTICATION_BACKENDS = ( + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", +) +# https://django-allauth.readthedocs.io/en/latest/configuration.html +ACCOUNT_SESSION_REMEMBER = True +ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False +ACCOUNT_USERNAME_REQUIRED = False +ACCOUNT_AUTHENTICATION_METHOD = "email" +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_UNIQUE_EMAIL = True diff --git a/django_project/urls.py b/django_project/urls.py new file mode 100644 index 0000000..dc27b89 --- /dev/null +++ b/django_project/urls.py @@ -0,0 +1,16 @@ +from django.conf import settings +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path("admin/", admin.site.urls), + path("accounts/", include("allauth.urls")), + path("", include("pages.urls")), +] + +if settings.DEBUG: + import debug_toolbar + + urlpatterns = [ + path("__debug__/", include(debug_toolbar.urls)), + ] + urlpatterns diff --git a/djangox/wsgi.py b/django_project/wsgi.py similarity index 57% rename from djangox/wsgi.py rename to django_project/wsgi.py index e6aa2b3..82acc09 100644 --- a/djangox/wsgi.py +++ b/django_project/wsgi.py @@ -2,6 +2,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangox.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_project.settings") application = get_wsgi_application() diff --git a/djangox/settings.py b/djangox/settings.py deleted file mode 100644 index ca31718..0000000 --- a/djangox/settings.py +++ /dev/null @@ -1,145 +0,0 @@ -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.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '4(j*7q=-dm@4&d8hb)-ivy#b(&_3ew19ujzo#h_hq-39!6-5d+' - -# 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', - 'django.contrib.sites', - - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - # 'allauth.socialaccount.providers.google', - 'crispy_forms', - - 'users', - 'pages', -] - -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 = 'djangox.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 = 'djangox.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/2.0/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.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/2.0/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.0/howto/static-files/ - -STATIC_URL = '/static/' -STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] - -# Bootstrap Crispy-Forms config -CRISPY_TEMPLATE_PACK = 'bootstrap4' - -# Authentication settings -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' - -AUTHENTICATION_BACKENDS = ( - "django.contrib.auth.backends.ModelBackend", - "allauth.account.auth_backends.AuthenticationBackend", -) - -AUTH_USER_MODEL = 'users.CustomUser' -SITE_ID = 1 -LOGIN_REDIRECT_URL = 'home' -ACCOUNT_LOGOUT_REDIRECT_URL = 'home' -ACCOUNT_AUTHENTICATION_METHOD = 'email' -ACCOUNT_USERNAME_REQUIRED = False -ACCOUNT_EMAIL_REQUIRED = True -ACCOUNT_USERNAME_MIN_LENGTH = 3 -ACCOUNT_EMAIL_VERIFICATION = 'optional' # set this to 'mandatory'? -ACCOUNT_UNIQUE_EMAIL = True -# ACCOUNT_USER_MODEL_EMAIL_FIELD = 'email' -ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True -ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False -ACCOUNT_SESSION_REMEMBER = True diff --git a/djangox/urls.py b/djangox/urls.py deleted file mode 100644 index 755dc21..0000000 --- a/djangox/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.contrib import admin -from django.urls import include, path - -urlpatterns = [ - path('', include('pages.urls')), - - # Django Admin - path('admin/', admin.site.urls), - - # User management - path('users/', include('users.urls')), - path('accounts/', include('allauth.urls')), # new -] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1144ac8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + web: + build: . + command: python /code/manage.py runserver 0.0.0.0:8000 + volumes: + - .:/code + ports: + - 8000:8000 + depends_on: + - db + db: + image: postgres:16 + volumes: + - postgres_data:/var/lib/postgresql/data/ + environment: + - "POSTGRES_HOST_AUTH_METHOD=trust" + +volumes: + postgres_data: diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..a3ed5a3 Binary files /dev/null and b/logo.png differ diff --git a/manage.py b/manage.py index cac2990..fac3788 100755 --- a/manage.py +++ b/manage.py @@ -1,9 +1,12 @@ #!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" import os import sys -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangox.settings") + +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: @@ -13,3 +16,7 @@ if __name__ == "__main__": "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/notes.md b/notes.md deleted file mode 100644 index d2e2212..0000000 --- a/notes.md +++ /dev/null @@ -1,6 +0,0 @@ -- tut on custom/auth... - -- add about page... -- add base.html with nav, update other templates -- add 404, 403, 500 pages -- customize password reset... \ No newline at end of file diff --git a/pages/apps.py b/pages/apps.py index acdb960..344e0f0 100644 --- a/pages/apps.py +++ b/pages/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class PagesConfig(AppConfig): - name = 'pages' + name = "pages" diff --git a/pages/urls.py b/pages/urls.py index 403ff04..b4f7357 100644 --- a/pages/urls.py +++ b/pages/urls.py @@ -1,8 +1,8 @@ from django.urls import path -from . import views +from .views import HomePageView, AboutPageView urlpatterns = [ - path('', views.HomePageView.as_view(), name='home'), - path('about/', views.AboutPageView.as_view(), name='about'), + path("", HomePageView.as_view(), name="home"), + path("about/", AboutPageView.as_view(), name="about"), ] diff --git a/pages/views.py b/pages/views.py index 52d79a3..886a2ba 100644 --- a/pages/views.py +++ b/pages/views.py @@ -2,8 +2,8 @@ from django.views.generic import TemplateView class HomePageView(TemplateView): - template_name = 'pages/home.html' + template_name = "pages/home.html" class AboutPageView(TemplateView): - template_name = 'pages/about.html' + template_name = "pages/about.html" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4a46de5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,26 @@ +asgiref==3.8.1 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==3.0.1 +crispy-bootstrap5==2024.10 +cryptography==39.0.1 +defusedxml==0.7.1 +Django==5.1.2 +django-allauth==65.0.2 +django-crispy-forms==2.3 +django-debug-toolbar==4.4.6 +gunicorn==23.0.0 +idna==3.4 +oauthlib==3.2.2 +packaging==23.1 +psycopg==3.2.3 +psycopg-binary==3.2.3 +pycparser==2.21 +PyJWT==2.6.0 +python3-openid==3.2.0 +requests==2.28.2 +requests-oauthlib==1.3.1 +sqlparse==0.4.3 +typing_extensions==4.9.0 +urllib3==1.26.14 +whitenoise==6.7.0 diff --git a/static/css/base.css b/static/css/base.css new file mode 100644 index 0000000..d32992a --- /dev/null +++ b/static/css/base.css @@ -0,0 +1,33 @@ +/* Sticky footer styles +-------------------------------------------------- */ +html { + position: relative; + min-height: 100%; + font-size: 14px; +} +@media (min-width: 768px) { + html { + font-size: 16px; + } +} + +body { + margin-bottom: 60px; /* Margin bottom by footer height */ +} + +.container { + max-width: 960px; +} + +.pricing-header { + max-width: 700px; +} + +.footer { + position: absolute; + bottom: 0; + width: 100%; + height: 60px; /* Set the fixed height of the footer here */ + line-height: 60px; /* Vertically center the text there */ + background-color: #f5f5f5; +} diff --git a/static/images/falconx.png b/static/images/falconx.png deleted file mode 100644 index b4b5ee4..0000000 Binary files a/static/images/falconx.png and /dev/null differ diff --git a/static/images/logo.png b/static/images/logo.png new file mode 100644 index 0000000..a3ed5a3 Binary files /dev/null and b/static/images/logo.png differ diff --git a/static/js/base.js b/static/js/base.js new file mode 100644 index 0000000..e69de29 diff --git a/templates/403_csrf.html b/templates/403_csrf.html index 2e2a9a7..46d63f0 100644 --- a/templates/403_csrf.html +++ b/templates/403_csrf.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% block title %}Forbidden (403){% endblock title %} diff --git a/templates/404.html b/templates/404.html index 6fde634..91110b3 100644 --- a/templates/404.html +++ b/templates/404.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% block title %}404 Page not found{% endblock %} diff --git a/templates/500.html b/templates/500.html index b47dac1..ae82ffd 100644 --- a/templates/500.html +++ b/templates/500.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% block title %}500 Server Error{% endblock %} diff --git a/templates/_base.html b/templates/_base.html new file mode 100644 index 0000000..81a7140 --- /dev/null +++ b/templates/_base.html @@ -0,0 +1,95 @@ +{% load static %} + + + + + + + + {% block title %}DjangoX{% endblock title %} + + + + + {% block css %} + + + + + {% endblock %} + + + + + +
+ {% block content %} +

Default content...

+ {% endblock content %} +
+ + + + {% block javascript %} + + + + + + + {% endblock javascript %} + + + \ No newline at end of file diff --git a/templates/account/login.html b/templates/account/login.html index e50a4f8..10fe7bf 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Log in{% endblock %} @@ -10,5 +10,4 @@ {{ form|crispy }} -Forgot Password? -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/templates/account/logout.html b/templates/account/logout.html new file mode 100644 index 0000000..5eb9747 --- /dev/null +++ b/templates/account/logout.html @@ -0,0 +1,18 @@ +{% extends '_base.html' %} +{% load crispy_forms_tags %} + +{% block title %}Log out{% endblock %} + + +{% block content %} +

Sign Out

+ +

Are you sure you want to sign out?

+ +
+ {% csrf_token %} + {{ form|crispy }} + +
+ +{% endblock content %} \ No newline at end of file diff --git a/templates/account/password_change.html b/templates/account/password_change.html index ba2a68c..937c99a 100644 --- a/templates/account/password_change.html +++ b/templates/account/password_change.html @@ -1,13 +1,13 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Change Password{% endblock %} {% block content %}

Change Password

-
+ {% csrf_token %} {{ form|crispy }}
-{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/templates/account/password_reset.html b/templates/account/password_reset.html index e159f29..be2c91d 100644 --- a/templates/account/password_reset.html +++ b/templates/account/password_reset.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Password Reset{% endblock %} @@ -7,9 +7,7 @@

Forgot your password?

{% csrf_token %} - {{ form|crispy }} + {{ form | crispy }}
{% endblock content %} - - diff --git a/templates/account/password_reset_done.html b/templates/account/password_reset_done.html index 38cd134..2e6f3d3 100644 --- a/templates/account/password_reset_done.html +++ b/templates/account/password_reset_done.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Password Reset Done{% endblock %} diff --git a/templates/account/password_reset_from_key.html b/templates/account/password_reset_from_key.html index c142b19..fb9eeb3 100644 --- a/templates/account/password_reset_from_key.html +++ b/templates/account/password_reset_from_key.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Change Password{% endblock title %} diff --git a/templates/account/password_reset_from_key_done.html b/templates/account/password_reset_from_key_done.html index 4b59c6d..58c4094 100644 --- a/templates/account/password_reset_from_key_done.html +++ b/templates/account/password_reset_from_key_done.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Change Password Done{% endblock title %} diff --git a/templates/account/password_set.html b/templates/account/password_set.html index 37f5e86..a46b8f1 100644 --- a/templates/account/password_set.html +++ b/templates/account/password_set.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Set Password{% endblock title %} @@ -6,9 +6,10 @@ {% block content %}
{% csrf_token %} - {% bootstrap_form password_set_form %} + {{ form | crispy }}
- +
{% endblock content %} diff --git a/templates/account/signup.html b/templates/account/signup.html index c98bbb3..da021a9 100644 --- a/templates/account/signup.html +++ b/templates/account/signup.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load crispy_forms_tags %} {% block title %}Sign up{% endblock %} diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 1440d9e..0000000 --- a/templates/base.html +++ /dev/null @@ -1,76 +0,0 @@ -{% load static %} -{% load socialaccount %} - - - - - - -{% block title %}DjangoX{% endblock title %} - - - - -{% block css %} - - - - -{% endblock %} - - - - - - - -
- {% block content %} -

Default content...

- {% endblock content %} -
- - - - - {% block javascript %} - - - - - - - - - - - {% endblock javascript %} - - - \ No newline at end of file diff --git a/templates/pages/about.html b/templates/pages/about.html index 90282c2..ec8bc4e 100644 --- a/templates/pages/about.html +++ b/templates/pages/about.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% block title %}About page{% endblock %} diff --git a/templates/pages/home.html b/templates/pages/home.html index e5f32ae..2169f64 100644 --- a/templates/pages/home.html +++ b/templates/pages/home.html @@ -1,10 +1,11 @@ -{% extends 'base.html' %} +{% extends '_base.html' %} {% load static %} {% block title %}Home page{% endblock title %} {% block content %} -

Home page

-FalconX launch +
+ DjangoX logo +

A Django starter project with batteries.

+
{% endblock content %} - diff --git a/templates/registration/login.html b/templates/registration/login.html deleted file mode 100644 index 91099ac..0000000 --- a/templates/registration/login.html +++ /dev/null @@ -1,6 +0,0 @@ -

Login

-
- {% csrf_token %} - {{ form.as_p }} - -
\ No newline at end of file diff --git a/users/apps.py b/users/apps.py deleted file mode 100644 index 4ce1fab..0000000 --- a/users/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class UsersConfig(AppConfig): - name = 'users' diff --git a/users/models.py b/users/models.py deleted file mode 100644 index 8b840a8..0000000 --- a/users/models.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.contrib.auth.models import AbstractUser, UserManager - - -class CustomUserManager(UserManager): - pass - - -class CustomUser(AbstractUser): - objects = CustomUserManager() diff --git a/users/urls.py b/users/urls.py deleted file mode 100644 index 5d4b73f..0000000 --- a/users/urls.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.urls import path -from . import views - -urlpatterns = [ - path('signup/', views.SignUp.as_view(), name='signup'), -] \ No newline at end of file diff --git a/users/views.py b/users/views.py deleted file mode 100644 index 870b748..0000000 --- a/users/views.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.urls import reverse_lazy -from django.views import generic - -from .forms import CustomUserCreationForm - - -class SignUp(generic.CreateView): - form_class = CustomUserCreationForm - success_url = reverse_lazy('login') - template_name = 'account/signup.html'