User Profiles:

Manage user profiles

create app profiles:

  • create a user profile app

    1. [root@localhost Twittme]# python3 ./manage.py startapp profiles
  • define the model in /profiles/models.py ```python from django.db import models from django.conf import settings

User = settings.AUTH_USER_MODEL

class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) # A OneToOneField is essentially the same as a ForeignKey location = models.CharField(max_length=220, null=True, blank=True) # allow null and blank bio = models.TextField(blank=True, null=True)

  1. - register the app in /Twittme/settings.py
  2. ```python
  3. INSTALLED_APPS = [
  4. 'django.contrib.admin',
  5. 'django.contrib.auth',
  6. 'django.contrib.contenttypes',
  7. 'django.contrib.sessions',
  8. 'django.contrib.messages',
  9. 'django.contrib.staticfiles',
  10. # third-party
  11. 'rest_framework',
  12. 'corsheaders',
  13. # internal
  14. 'accounts',
  15. 'profiles',
  16. 'tweets',
  17. ]
  • because we have new models created, make migrations and migrate

    1. [root@localhost Twittme]# python3 ./manage.py makemigrations
    2. Migrations for 'profiles':
    3. profiles/migrations/0001_initial.py
    4. - Create model Profile
    5. [root@localhost Twittme]# python3 ./manage.py migrate
    6. Operations to perform:
    7. Apply all migrations: admin, auth, contenttypes, profiles, sessions, tweets
    8. Running migrations:
    9. Applying profiles.0001_initial... OK

    use another url file to handle the path:

  • create /profiles/urls.py ```python from django.urls import path

from .views import profile_detail_view

urlpatterns = [ path(‘‘, profile_detail_view), # profile_detail_view will be created later ]

  1. - because we are creating a profile handler remove tweets_profile_view in /tweets/views.py
  2. ```python
  3. # def tweets_profile_view(request, username, *args, **kwargs):
  4. # return render(request, "tweets/profile.html", context={"profile_username": username})
  • modify /Twittme/urls.py ```python from tweets.views import ( tweets_list_view, tweets_detail_view,

    tweets_profile_view,

    )

urlpatterns = [ path(‘admin/‘, admin.site.urls), path(‘api/tweets/‘, include(‘tweets.api.urls’)), path(‘’, tweets_list_view), path(‘login/‘, login_view), path(‘logout/‘, logout_view), path(‘register/‘, register_view), path(‘‘, tweets_detail_view),

  1. # path('profile/<str:username>', tweets_profile_view),
  2. re_path(r'profiles?/', include('profiles.urls')), # redirect to /profiles/urls.py

]

  1. - create directory /templates/profiles
  2. - move /templates/tweets/profile.html into /templates/profiles
  3. - modify and rename it into detail.html
  4. ```html
  5. {% extends 'base.html' %}
  6. {% block content %}
  7. <!--
  8. <div id='tweetme-2'
  9. data-username="{{ profile_username }}"
  10. data-can-tweet="false"></div>
  11. -->
  12. <div id='tweetme-2'
  13. data-username="{{ username }}"
  14. data-can-tweet="false"></div>
  15. {% endblock content %}

to test, runserver and access

http://localhost/profiles/[username]

if that user exists, it will be a tweet list of those tweets sent by that user
image.png
if that user does not exist, it will show an empty tweet list.
image.png

Handling Profile Does Not Exist:

This case is to handle the last situation “if the user that send request about the profile does not exist”

import 404:

  • import Http404 in /profiles/views.py ```python from django.shortcuts import render from django.http import Http404

from .models import Profile

def profile_detail_view(request, username, args, *kwargs):

  1. # get the profile for passed username
  2. qs = Profile.objects.filter(user__username=username)
  3. if not qs.exists(): # if user don't exist, return 404
  4. raise Http404
  5. profile_obj = qs.first() # pick up profile objects
  6. context = {
  7. "username": username,
  8. "profile": profile_obj,
  9. }
  10. # return render(request, "profiles/detail.html", {"username": username})
  11. return render(request, "profiles/detail.html", context)
  1. to test, runserver and try to access a profile with a user that does not exist<br />successfully get status 404.<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595792331150-86a4bc71-4b0e-47c8-ac17-980b5ce80526.png#align=left&display=inline&height=474&margin=%5Bobject%20Object%5D&name=image.png&originHeight=948&originWidth=1790&size=83727&status=done&style=none&width=895)
  2. <a name="oDgUG"></a>
  3. ### Signals to Create Profile Objects:
  4. There are some built in signals that can be sent when a specific actions is done, such as creating new users. <br />We will apply signals to create profile for new created news.
  5. - add signal post_save and function user_did_save() in /profiles/models.py
  6. ```python
  7. # ...
  8. from django.db.models.signals import post_save # new
  9. User = settings.AUTH_USER_MODEL
  10. # ...
  11. def user_did_save(sender, instance, created, *args, **kwargs): # new
  12. Profile.objects.get_or_create(user=instance)
  13. if created:
  14. Profile.objects.get_or_create(user=instance)
  15. post_save.connect(user_did_save, sender=User) # new

image.png

  • to double check, modify /profiles/admin.py ```python from django.contrib import admin

from .models import Profile

admin.site.register(Profile)

  1. reboot the server, access admin page<br />and we have a profile list, and each profile is corresponding to a user. <br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595793878255-7d711981-2de7-4577-b332-7b10a7c79663.png#align=left&display=inline&height=437&margin=%5Bobject%20Object%5D&name=image.png&originHeight=873&originWidth=1089&size=75043&status=done&style=none&width=544.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595793890943-9b81d782-631c-4fac-bd84-be7048291539.png#align=left&display=inline&height=430&margin=%5Bobject%20Object%5D&name=image.png&originHeight=859&originWidth=1066&size=59589&status=done&style=none&width=533)
  2. - remove the first objects.get_or_create() cause we already have profiles for current users in /profiles/models.py
  3. ```python
  4. # ...
  5. def user_did_save(sender, instance, created, *args, **kwargs):
  6. # Profile.objects.get_or_create(user=instance)
  7. if created:
  8. Profile.objects.get_or_create(user=instance)
  9. post_save.connect(user_did_save, sender=User)
  • in /profiles/models.py ```python

    from django.db.models.signals import post_save # new

User = settings.AUTH_USER_MODEL

def user_did_save(sender, instance, created, args, *kwargs): # new Profile.objects.get_or_create(user=instance) if created: Profile.objects.get_or_create(user=instance)

post_save.connect(user_did_save, sender=User) # new

  1. to test, create another user<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595794104648-2909583f-5edb-4aa1-8be2-2b5630b7cc88.png#align=left&display=inline&height=427&margin=%5Bobject%20Object%5D&name=image.png&originHeight=854&originWidth=1652&size=68216&status=done&style=none&width=826)<br />and see "Profiles" in admin site<br />a profile which the user is the newly created one is created. <br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595794145723-1a9d0cc3-e4ac-4fe5-ab41-5b97421db4e5.png#align=left&display=inline&height=341&margin=%5Bobject%20Object%5D&name=image.png&originHeight=681&originWidth=787&size=48098&status=done&style=none&width=393.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595794157999-5c7255df-8d5b-474d-a165-fe951e1d4dc5.png#align=left&display=inline&height=386&margin=%5Bobject%20Object%5D&name=image.png&originHeight=771&originWidth=1040&size=44388&status=done&style=none&width=520)
  2. <a name="etg56"></a>
  3. ### Save 2 Models in 1 Form and 1 View:
  4. - create /profiles/forms.py
  5. ```python
  6. from django import forms
  7. from django.contrib.auth import get_user_model
  8. from .models import Profile
  9. User = get_user_model()
  10. class ProfileForm(forms.ModelForm):
  11. first_name = forms.CharField(required=False)
  12. last_name = forms.CharField(required=False)
  13. email_address = forms.CharField(required=False)
  14. class Meta:
  15. model = Profile
  16. fields = ['location', 'bio']

and in admin site user page, we have a “personal info”
image.png

  • add profile_update_view in /profiles/views.py ```python from django.shortcuts import render from django.http import Http404

from .models import Profile from .forms import ProfileForm # new

def profile_update_view(request, args, *kwargs): # new if not request.user.is_authenticated: # is_authenticated() return redirect(“/login?next=/profile/update”) user = request.user my_profile = request.user.profile form = ProfileForm(request.POST or None, instance=my_profile) if form.is_valid(): profile_obj = form.save(commit=False) first_name = form.cleaned_data.get(‘first_name’) last_name = form.cleaned_data.get(‘last_name’) email_address = form.cleaned_data.get(‘email_address’) user.first_name = first_name user.last_name = last_name user.email_address = email_address user.save() profile_obj.save() context = { “form”: form, “btn_label”: “Save”, “title”: “Update Profile” } return render(request, “profiles/form.html”, context)

  1. - create rendered HTML file /templates/profiles/form.html
  2. ```html
  3. {% extends "base.html" %}
  4. {% block content %}
  5. <div class='col-10 col-md-4 mx-auto'>
  6. <h1>{{ title }}</h1>
  7. {% if description %}
  8. <p>{{ description }}</p>
  9. {% endif %}
  10. {% include "components/form.html" with form=form btn_label=btn_label %}
  11. </div>
  12. {% endblock content %}
  • add a path in /profiles/urls.py ```python from django.urls import path

from .views import profile_detail_view, profile_update_view # new

urlpatterns = [ path(‘edit’, profile_update_view), # new path(‘‘, profile_detail_view), ]

  1. to test, runserver<br />access [http://localhost/profile/edit](http://localhost/profile/edit/)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1243266/1595872408297-e3a2c6e8-ade1-484d-a3d7-44a8ebab691f.png#align=left&display=inline&height=452&margin=%5Bobject%20Object%5D&name=image.png&originHeight=903&originWidth=1459&size=48331&status=done&style=none&width=729.5)<br />we have a update profile here.
  2. - we separate 3 forms for different uses in /profiles/forms.py
  3. ```python
  4. from django import forms
  5. from django.contrib.auth import get_user_model
  6. from .models import Profile
  7. User = get_user_model()
  8. # class ProfileForm(forms.ModelForm):
  9. # first_name = forms.CharField(required=False)
  10. # last_name = forms.CharField(required=False)
  11. # email_address = forms.CharField(required=False)
  12. # class Meta:
  13. # model = Profile
  14. # fields = ['location', 'bio']
  15. # new
  16. class UserProfileForm(forms.ModelForm):
  17. location = forms.CharField(required=False)
  18. bio = forms.CharField(required=False)
  19. class Meta:
  20. model = User
  21. fields = ['first_name', 'last_name', 'email']
  22. # new
  23. class ProfileForm(forms.ModelForm):
  24. first_name = forms.CharField(required=False)
  25. last_name = forms.CharField(required=False)
  26. email = forms.CharField(required=False)
  27. class Meta:
  28. model = Profile
  29. fields = ['location', 'bio']
  30. # new
  31. class ProfileBasicForm(forms.Form):
  32. first_name = forms.CharField(required=False)
  33. last_name = forms.CharField(required=False)
  34. email_address = forms.CharField(required=False)
  35. location = forms.CharField(required=False)
  36. bio = forms.CharField(required=False)
  • changed name in and /profiles/views.py
    1. def profile_update_view(request, *args, **kwargs):
    2. if not request.user.is_authenticated: # is_authenticated()
    3. return redirect("/login?next=/profile/update")
    4. user = request.user
    5. user_data = { # new
    6. "first_name": user.first_name,
    7. "last_name": user.last_name,
    8. "email": user.email
    9. }
    10. my_profile = request.user.profile
    11. # form = ProfileForm(request.POST or None, instance=my_profile)
    12. form = ProfileForm(request.POST or None,
    13. instance=my_profile, initial=user_data) # fill in hse data if exists
    14. if form.is_valid():
    15. profile_obj = form.save(commit=False)
    16. first_name = form.cleaned_data.get('first_name')
    17. last_name = form.cleaned_data.get('last_name')
    18. # email_address = form.cleaned_data.get('email_address')
    19. email = form.cleaned_data.get('email') # to fit changed name
    20. user.first_name = first_name
    21. user.last_name = last_name
    22. # user.email_address = email_address
    23. user.email = email # to fit changed name
    24. user.save()
    25. profile_obj.save()
    26. context = {
    27. "form": form,
    28. "btn_label": "Save",
    29. "title": "Update Profile"
    30. }
    31. return render(request, "profiles/form.html", context)
    to test, reboot the server and access http://localhost/profile/edit
    we have a prepared first and last name
    image.png
    fill in something
    image.png
    save, and check admin site:
    image.png
    the edit step is successful.