{"id":20455271,"url":"https://github.com/alexmhack/django-signup","last_synced_at":"2025-04-13T03:36:03.471Z","repository":{"id":44826593,"uuid":"150583443","full_name":"Alexmhack/Django-Signup","owner":"Alexmhack","description":"Creating a simple sign up view and then moving onto more advanced sign up view with profile model and confirmation mail","archived":false,"fork":false,"pushed_at":"2024-03-11T15:25:25.000Z","size":1620,"stargazers_count":36,"open_issues_count":0,"forks_count":22,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-04T05:03:28.822Z","etag":null,"topics":["beginner-project","beginner-tutorial","django2","django2-tutorial","python3","reusable"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Alexmhack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-27T12:29:47.000Z","updated_at":"2024-11-22T20:52:23.000Z","dependencies_parsed_at":"2022-07-14T19:30:39.509Z","dependency_job_id":null,"html_url":"https://github.com/Alexmhack/Django-Signup","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexmhack%2FDjango-Signup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexmhack%2FDjango-Signup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexmhack%2FDjango-Signup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alexmhack%2FDjango-Signup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Alexmhack","download_url":"https://codeload.github.com/Alexmhack/Django-Signup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248660046,"owners_count":21141230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beginner-project","beginner-tutorial","django2","django2-tutorial","python3","reusable"],"created_at":"2024-11-15T11:18:24.658Z","updated_at":"2025-04-13T03:36:03.440Z","avatar_url":"https://github.com/Alexmhack.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django-Signup\nCreating a simple sign up view and then moving onto more advanced sign up view with profile model and confirmation mail\n\n**I have also added a location field in custom profile model and stored the \nactual location (city, country code) of user using API, a little javascript and \npython without letting user know about it. For a tutorial on using that API checkout my [repo](https://github.com/Alexmhack/django_weather_app)**\n\nIn this tutorial we will \n\n1. Create basic sign up view\n2. Create sign up form with extra fields\n3. Create profile model for users\n4. Create sign up view with confirmation email\n\n# Finished View\nAfter completing the project and adding some styling from [mdbootstrap](http://mdbootstrap.com/) my project looks like\n\nHome Page is simply showing a navbar with working links\n\n![Home Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/home.PNG)\n\nLogin page looks like \n\n![signin Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/signin.PNG)\n\nSign Up page\n\n![signup Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/signup.PNG)\n\nAnd finally dashboard page with the user name displayed...\n\n![dashboard Page](https://github.com/Alexmhack/Django-Signup/blob/master/readme_images/dashboard.PNG)\n\n**You can reuse the code from my repo and include it in your django project. Put a star if you liked my work.**\n\n# Django Project Setup\n\n```\npip install -r requirements.txt\n```\n\n1. ```django-admin startproject website .```\n2. ```python manage.py migrate```\n3. ```python manage.py createsuperuser```\n4. ```python manage.py runserver```\n5. In project ```settings.py``` file import [decouple](https://pypi.org/project/python-decouple/)\n\n\t```\n\tfrom decouple import config\n\tSECRET_KEY = config(\"PROJECT_KEY\")\n\t```\n\n\tAnd create ```.env``` file like so\n\n\t```\n\tPROJECT_KEY=93%@nka8)+fv-*ai-st1d*h)w2j2-y^)(jfiv9bogcy0u241u7\n\t```\n\nWe will start with the basic sign up features that django provides by default\n\n# Basic Sign Up\nSimplest way to implement a **Sign Up** view is using ```UserCreationForm```. \nThis form is for those django apps which use the default user model that only \ncontains a **username** and **password** for user sign ups.\n\nTo implement that view we need\n\n**urls.py**\n```\n...\nfrom .views import signup_view\n\nurlpatterns = [\n    path('admin/', admin.site.urls),\n    path('signup/', signup_view, name='sign-up'),\n]\n```\n\nIn ```urls.py``` we simply import the ```signup_view``` that we haven't yet added \nto ```views.py``` and create a url for that view.\n\nNow create ```views.py``` file in the **website** folder and put this code inside \nit.\n\n```\nfrom django.shortcuts import render, redirect\nfrom django.contrib.auth import authenticate, login\nfrom django.contrib.auth.forms import UserCreationForm\n\ndef signup(request):\n\tif request.method == \"POST\":\n\t\tform = UserCreationForm(request.POST)\n\t\tif form.is_valid():\n\t\t\tform.save()\n\t\t\tusername = form.cleaned_data.get('username')\n\t\t\tpassword = form.cleaned_data.get('password1')\n\t\t\tuser = authenticate(username=username, password=password)\n\t\t\tlogin(request, user)\n\t\t\treturn redirect('home')\n\t\telse:\n\t\t\tform = UserCreationForm()\n\n\t\treturn render(request, 'signup.html', {'form': form})\n```\n\nThis is most basic signup view that django has. All of the new user creation \nprocess is done using django. We use the default ```UserCreationForm``` form to \ndisplay the signup form. We authenticate the new user using the username and \npassword that we get from the post request from the **form**. We then login the \nuser and redirect it to ```home``` view. If the request method is not ```POST``` \nwe simply show the empty form in ```templates/signup.html``` file.\n\nCreate a **templates** folder in root path (where ```manage.py``` file lies). In\nthat folder create ```signup.html``` file.\n\n**signup.html**\n```\n{% extends 'base.html' %}\n\n{% block content %}\n  \u003ch2\u003eSign up\u003c/h2\u003e\n  \u003cform method=\"post\"\u003e\n    {% csrf_token %}\n    {% for field in form %}\n      \u003cp\u003e\n        {{ field.label_tag }}\u003cbr\u003e\n        {{ field }}\n        {% if field.help_text %}\n          \u003csmall style=\"color: grey\"\u003e{{ field.help_text }}\u003c/small\u003e\n        {% endif %}\n        {% for error in field.errors %}\n          \u003cp style=\"color: red\"\u003e{{ error }}\u003c/p\u003e\n        {% endfor %}\n      \u003c/p\u003e\n    {% endfor %}\n    \u003cbutton type=\"submit\"\u003eSign up\u003c/button\u003e\n  \u003c/form\u003e\n{% endblock %}\n```\n\nThis is a little different way of rendering form. There are more ways like\n\n```\n{{ form.as_p }} \n{{ form.as_table }}\n{{ form.as_ul }} \n```\n\n# Sign Up Form With Extra Fields\nSo far we have been using the default fields that ```UserCreationForm``` provides\nus. But what if we wanted the email address of the new user which is the \nimportant part aside from the username and password. \n\nFor that we can inherit a new form class from ```UserCreationForm```. Create a \nnew file named ```forms.py``` in the **website** folder. All of this should go \nin a separate app so you can start another app. Let's just do that as our project\nis increasing in size.\n\n```\npython manage.py startapp users\n```\n\nInclude app in project **settings**\n\n```\n...\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'users',\n]\n```\n\nMove the file ```website/views.py``` to ```users``` folder and replace it.\nThese are some of the changes which you would have to do a lot of times if you \nare working on a large project in any language.\n\nSince we moved the ```views.py``` file, all our imports in ```urls.py``` will \ngive errors. Let's also create a ```urls.py``` file in ```users``` folder.\n\nFrom the **website/urls.py** file change this piece of code into\n\n```\nfrom .views import signup_view, dashboard_view, home_view\n\nurlpatterns = [\n    path('admin/', admin.site.urls),\n    path('signup/', signup_view, name='sign-up'),\n    path('dashboard/', dashboard_view, name='dashboard'),\n    path('', home_view, name='home'),\n]\n```\n\nreplace with below code\n\n```\nfrom django.urls import path, include\n\nurlpatterns = [\n    path('admin/', admin.site.urls),\n    path('', include('users.urls', namespace='users')),\n]\n```\n\nNow we will create a custom user registration form so create a new file ```forms.py``` in **users** folder.\n\n```\nfrom django import forms\nfrom django.contrib.auth.forms import UserCreationForm\nfrom django.contrib.auth.models import User\n\nclass SignUpForm(UserCreationForm):\n\tfirst_name = forms.CharField(\n\t\tmax_length=10,\n\t\tmin_length=4,\n\t\trequired=True,\n\t\twidget=forms.TextInput(\n\t\t\t\tattrs={\n\t\t\t\t\t\"placeholder\": \"First Name\",\n\t\t\t\t\t\"class\": \"form-control\"\n\t\t\t\t}\n\t\t\t)\n\t\t)\n\tlast_name = forms.CharField(\n\t\tmax_length=30,\n\t\trequired=True,\n\t\twidget=forms.TextInput(\n\t\t\t\tattrs={\n\t\t\t\t\t\"placeholder\": \"Last Name\",\n\t\t\t\t\t\"class\": \"form-control\"\n\t\t\t\t}\n\t\t\t)\n\t\t)\n\temail = forms.EmailField(\n\t\tmax_length=254,\n\t\twidget=forms.EmailInput(\n\t\t\tattrs={\n\t\t\t\t\"placeholder\": \"Email\",\n\t\t\t\t\"class\": \"form-control\"\n\t\t\t}\n\t\t)\n\t)\n\n\tclass Meta:\n\t\tmodel = User\n\t\tfields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2',)\n```\n\nYou can do the same with ```password``` fields by using ```PasswordInput()```\n\n```\n...\n\tpassword1 = forms.CharField(\n\t\tlabel='',\n\t\tmax_length=30,\n\t\tmin_length=8,\n\t\trequired=True,\n\t\twidget=forms.PasswordInput(\n\t\t\tattrs={\n\t\t\t\t\"placeholder\": \"Password\",\n\t\t\t\t\"class\": \"form-control\"\n\t\t\t}\n\t\t)\n\t)\n\n\tpassword2 = forms.CharField(\n\t\tlabel='',\n\t\tmax_length=30,\n\t\tmin_length=8,\n\t\trequired=True,\n\t\twidget=forms.PasswordInput(\n\t\t\tattrs={\n\t\t\t\t\"placeholder\": \"Confirm Password\",\n\t\t\t\t\"class\": \"form-control\"\n\t\t\t}\n\t\t)\n\t)\n```\n\nThis would give the forms a nice look as well as placeholders and the rest of \nthe django password validation remains intact and active.\n\n# Sign Up With Profile Model\nSo far we have been using the ```User``` model from \n```django.contrib.auth.models``` that meets almost all needs but **Django** docs\nitself recommends using a custom model for users instead of the ```User``` so in\nthis section we will be making our own custom model for users and name it \n```Profile```\n\nFor this part we will start another app name ```profiles```\n\n```\npython manage.py startapp profiles\n```\n\nAdd profiles app in **settings**\n\nInside ```profiles/models.py``` add \n\n```\nfrom django.db import models\nfrom django.contrib.auth.models import User\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\n```\n\nIn this particular case, the profile is created using a Signal. It’s not \nmandatory, but usually it is a good way to implement it.\n\n```\nclass Profile(models.Model):\n\tuser = models.OneToOneField(User, on_delete=models.CASCADE)\n\tbio = models.CharField(max_length=50, blank=True)\n\tlocation = models.CharField(max_length=30, blank=True)\n\n\n@receiver(post_save, sender=User)\ndef update_user_profile(sender, instance, created, **kwargs):\n    if created:\n        Profile.objects.create(user=instance)\n    instance.profile.save()\n```\n\nThis is our custom model, ofcourse you can go far more further adding in birth\ndate, and profile image and lots more stuff, but for simplicity we are just using\nthree fields.\n\nNow we need to create a form so ```forms.py``` file should have\n\n```\nfrom django import forms\nfrom django.contrib.auth.forms import UserCreationForm\nfrom django.contrib.auth.models import User\n\nclass SignUpForm(UserCreationForm):\n    birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')\n\n    class Meta:\n        model = User\n        fields = ('username', 'birth_date', 'password1', 'password2', )\n```\n\nYou can add in attributes in the fields again like we did earlier.\n\nThere are a few changes that ```views.py``` file should have\n\n```\nfrom django.contrib.auth import login, authenticate\nfrom django.shortcuts import render, redirect\nfrom mysite.core.forms import SignUpForm\n\ndef signup(request):\n    if request.method == 'POST':\n        form = SignUpForm(request.POST)\n        if form.is_valid():\n            user = form.save()\n            user.refresh_from_db()  # load the profile instance created by the signal\n            user.profile.birth_date = form.cleaned_data.get('birth_date')\n            user.save()\n            raw_password = form.cleaned_data.get('password1')\n            user = authenticate(username=user.username, password=raw_password)\n            login(request, user)\n            return redirect('home')\n    else:\n        form = SignUpForm()\n    return render(request, 'signup.html', {'form': form})\n```\n\nBecause of the Signal handling the Profile creation, we have a synchronism issue \nhere. It is easily solved by calling the user.refresh_from_db() method. This \nwill cause a hard refresh from the database, which will retrieve the profile \ninstance.\n\nIf you don’t call user.refresh_from_db(), when you try to access the \nuser.profile, it will return None.\n\nAfter refreshing it user model, set the cleaned data to the fields that matter, \nand save the user model. The user save will trigger the profile save as well, \nthat’s why you don’t need to call user.profile.save(), instead you call just \nuser.save().\n\nYou can display the user details using \n\n```\n\t\u003ch1 class=\"mt-5 text-center\"\u003eWelcome {{ request.user }}\u003c/h1\u003e\n\t\u003cp class=\"text-left mt-5\"\u003eBio: {{ user.profile.bio }}\u003c/p\u003e\n\t\u003cp class=\"text-left\"\u003eLocation: {{ user.profile.location }}\u003c/p\u003e\n\t\u003cp class=\"text-left\"\u003eJoined: {{ user.profile.timestamp }}\u003c/p\u003e\n```\n\n**For customizing the forms you can use [django-widget-tweaks](https://pypi.org/project/django-widget-tweaks/)**\n\n# Signup With Confirmation Email\nDjango provides built-in system for sending emails. But first of test purposes \nwe will be using **Console Backend** for emails. \n\nAdd this settings in ```settings.py``` file\n\n```\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n```\n\nNow for checking if a user is authenticated we will create a field in the profile \nmodel to determine if the user is confirmed or not.\n\n**profiles/models.py**\n```\nclass Profile(models.Model):\n\t...\n\temail_confirmed = models.BooleanField(default=False)\n```\n\nAnd for creating a one time link using django we will create a new file ```tokens.py```\n\n```\nfrom django.contrib.auth.tokens import PasswordResetTokenGenerator\nfrom django.utils import six\n\nclass AccountActivationTokenGenerator(PasswordResetTokenGenerator):\n\tdef _make_hash_value(self, user, timestamp):\n\t\treturn (\n\t\t\tsix.text_type(user.pk) + six.text_type(timestamp) +\n\t\t\tsix.text_type(user.profile.email_confirmed)\n\t\t)\n\n\naccount_activation_token = AccountActivationTokenGenerator()\n```\n\nWe use the ```pk``` from the user ```timestamp``` and the ```email_confirmed``` field\nto create a token. We basically extended the PasswordResetTokenGenerator to create a \nunique token generator to confirm email addresses. This make use of your project’s \nSECRET_KEY, so it is a pretty safe and reliable method.\n\nNow we need to define views for account activation as well as account activation sent \nview\n\n```\ndef account_activation_sent_view(request):\n    return render(request, 'registration/account_activation_sent.html')\n\n\ndef account_activate(request, uidb64, token):\n    try:\n        uid = urlsafe_base64_decode(uidb64).decode()\n        print(uid)\n        user = User.objects.get(pk=uid)\n    except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e:\n        print(e)\n        user = None\n\n    if user is not None and account_activation_token.check_token(user, token):\n        user.is_active = True\n        user.profile.email_confirmed = True\n        user.save()\n        login(request, user)\n        return redirect('users:dashboard')\n    else:\n        return render(request, 'registration/account_activation_invalid.html')\n```\n\n```account_activation_sent_view``` is justfor redirecting users if their account activation url is wrong. The template **registration/account_activation_sent.html**\nwill be\n\n```\n{% extends \"base.html\" %}\n\n{% block title %}\n\t{{ block.super }} - Check Your Email Account\n{% endblock %}\n\n{% block content %}\n\n\t\u003ch3 class=\"text-center\"\u003eCheck your email account for verifying your django account.\u003c/h3\u003e\n\t\n{% endblock %}\n```\n\n```account_activate``` function simply fetches the ```uidb64``` and ```token```\nfrom the url and uses the **.check_token** function from ```AccountActivationTokenGenerator``` class which takes the user and token.\n\nYou can remove the print statements from the code, I use them for testing purposes \nwhile writing my code.\n\n**profiles/views.py**\n```\nUser = get_user_model()\n\ndef signup(request):\n    if request.method == 'POST':\n        form = SignUpForm(request.POST)\n        if form.is_valid():\n            user = form.save(commit=False)\n            user.is_active = False\n            user.save()\n            user = form.save()\n            current_site = get_current_site(request)\n            subject = \"Activate your Django Serives Account\"\n            message = render_to_string('registration/account_activation_email.html', {\n                'user': user,\n                'domain': current_site.domain,\n                'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),\n                'token': account_activation_token.make_token(user)\n            })\n            user.email_user(subject, message)\n            return redirect('profiles:account-activation-sent')\n    else:\n        form = SignUpForm()\n    return render(request, 'app/signup.html', {\n        'form': form,\n        'profile': True\n    })\n```\n\nThis is the code for sending email to the user email, notice we have removed \n```user.refresh_from_db()``` since we are using ```form.save(commit=False)```\nso you can use ```user.refresh_from_db()``` after saving the form\n\n```\n\t...\n\t\tif form.is_valid():\n            user = form.save(commit=False)\n            user.is_active = False\n            user.save()\n            user = form.save()\n            user.refresh_from_db()\n            # your code here\n           \tuser.save()\t\t# call save again\n           \t...\n```\n\nThis is it. We can extend our model by using the ```smtp``` as email backend that \nwill actually send email to the email provided by the user but it requires some more\nsettings to be defined in the **settings.py** file. For more details visit [docs](https://docs.djangoproject.com/en/2.1/topics/email/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexmhack%2Fdjango-signup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexmhack%2Fdjango-signup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexmhack%2Fdjango-signup/lists"}