Django Admin:
print the user to the terminal who send a request in /tweets/views.py
def home_view(request, *args, **kwargs):print(request.user)return render(request, "pages/home.html", context={}, status=200)
to test, runserver and see the terminal, and the user is
AnonymousUser
step 2, access http://localhost:8000/admin to use that root account we setup before

get back to http://localhost:8000/
the user is:
root
- we can create new users in http://localhost:8000/admin as we need

- register “Tweet” into admin page in /tweets/admin.py ```python from django.contrib import admin
Register your models here.
from .models import Tweet
admin.site.register(Tweet)
and we see Tweets here<br /><br />so it has all tweets data that we created before<br />- we use python shell to test some commands```bash(reactjs) [root@localhost Twittme]# ./manage.py shellPython 3.6.5 (default, Sep 10 2018, 09:39:42)[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linuxType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> from tweets.models import Tweet>>> qs = Tweet.objects.filter(content="abc") # tweets that content is "abc">>> qs<QuerySet []>>>> qs = Tweet.objects.filter(content="Hello World") # tweets that content is "Hello World">>> qs<QuerySet [<Tweet: Tweet object (1)>]>>>> qs = Tweet.objects.filter(user="1") # tweets that sent from user id 1>>> qs<QuerySet [<Tweet: Tweet object (48)>, <Tweet: Tweet object (47)>, <Tweet: Tweet object (46)>, <Tweet: Tweet object (45)>, <Tweet: Tweet object (44)>, <Tweet: Tweet object (43)>, <Tweet: Tweet object (42)>, <Tweet: Tweet object (41)>, <Tweet: Tweet object (40)>, <Tweet: Tweet object (39)>, <Tweet: Tweet object (38)>, <Tweet: Tweet object (37)>, <Tweet: Tweet object (36)>, <Tweet: Tweet object (35)>, <Tweet: Tweet object (34)>, <Tweet: Tweet object (33)>, <Tweet: Tweet object (32)>, <Tweet: Tweet object (31)>, <Tweet: Tweet object (30)>, <Tweet: Tweet object (29)>, '...(remaining elements truncated)...']>>>> qs = Tweet.objects.filter(user__username="root") # tweets taht sent from user name "root">>> qs<QuerySet [<Tweet: Tweet object (48)>, <Tweet: Tweet object (47)>, <Tweet: Tweet object (46)>, <Tweet: Tweet object (45)>, <Tweet: Tweet object (44)>, <Tweet: Tweet object (43)>, <Tweet: Tweet object (42)>, <Tweet: Tweet object (41)>, <Tweet: Tweet object (40)>, <Tweet: Tweet object (39)>, <Tweet: Tweet object (38)>, <Tweet: Tweet object (37)>, <Tweet: Tweet object (36)>, <Tweet: Tweet object (35)>, <Tweet: Tweet object (34)>, <Tweet: Tweet object (33)>, <Tweet: Tweet object (32)>, <Tweet: Tweet object (31)>, <Tweet: Tweet object (30)>, <Tweet: Tweet object (29)>, '...(remaining elements truncated)...']>>>> qs = Tweet.objects.filter(user__username__iexact="rOOt") # same as the previous one but ignore uppercase/lowercase>>> qs<QuerySet [<Tweet: Tweet object (48)>, <Tweet: Tweet object (47)>, <Tweet: Tweet object (46)>, <Tweet: Tweet object (45)>, <Tweet: Tweet object (44)>, <Tweet: Tweet object (43)>, <Tweet: Tweet object (42)>, <Tweet: Tweet object (41)>, <Tweet: Tweet object (40)>, <Tweet: Tweet object (39)>, <Tweet: Tweet object (38)>, <Tweet: Tweet object (37)>, <Tweet: Tweet object (36)>, <Tweet: Tweet object (35)>, <Tweet: Tweet object (34)>, <Tweet: Tweet object (33)>, <Tweet: Tweet object (32)>, <Tweet: Tweet object (31)>, <Tweet: Tweet object (30)>, <Tweet: Tweet object (29)>, '...(remaining elements truncated)...']>>>> Tweet.objects.filter(content__iexact="HeLLo WoRLd") # tweets that content is "Hello World" (ignore uppercase/lowercase)<QuerySet [<Tweet: Tweet object (1)>]>>>>
and we need to do those commands in admin.
- so add a new class in /tweets/admin.py ```python from django.contrib import admin
Register your models here.
from .models import Tweet
class TweetAdmin(admin.ModelAdmin): search_fields = [‘userusername’, ‘useremail’] # only allow searches about username and email class Meta: model = Tweet
admin.site.register(Tweet, TweetAdmin)
- also allow admin page to search content, and let it show the user who sent tweets```pythonfrom django.contrib import admin# Register your models here.from .models import Tweetclass TweetAdmin(admin.ModelAdmin):list_display = ['__str__', 'user']search_fields = ['content', 'user__username', 'user__email'] # only allow searches about content, username and emailclass Meta:model = Tweetadmin.site.register(Tweet, TweetAdmin)
to test, runserver:

1, 9, and 10 inlucdes phrase “Hello World”
- we can also let contents of tweets shown as title in /tweets/models.py ```python from django.db import models from django.conf import settings import random
User = settings.AUTH_USER_MODEL
class Tweet(models.Model):
# maps to SQL data# id = models.AutoField(primary_key=True)user = models.ForeignKey(User, on_delete=models.CASCADE) # many users can many tweetscontent = models.TextField(blank=True, null=True)image = models.FileField(upload_to="images/", blank=True, null=True)def __str__(self): # newreturn self.contentclass Meta:ordering = ['-id']def serialize(self):return {"id": self.id,"content": self.content,"likes": random.randint(0, 200)}
to test, runserver:<br /><a name="NWQHH"></a>#### Associate Authenticated User to Object:- first, log out- get back to the homepage to send something<br />got server error because not corresponding sender/user id.- to handle this, modify /tweets/views.py```pythondef home_view(request, *args, **kwargs):print(request.user or None) # newreturn render(request, "pages/home.html", context={}, status=200)def tweet_create_view(request, *args, **kwargs):if not request.user.is_authenticated: # newif request.is_ajax():return JsonResponse({}, status=401)return redirect(settings.LOGIN_URL)form = TweetForm(request.POST or None)print('post data is', request.POST)next_url = request.POST.get("next") or Noneif form.is_valid():obj = form.save(commit=False)obj.user = request.user or None # anonymous userobj.save()if request.is_ajax():return JsonResponse(obj.serialize(), status=201)if next_url != None and is_safe_url(next_url, ALLOWED_HOSTS):return redirect(next_url)form = TweetForm()if form.errors:if request.is_ajax():return JsonResponse(form.errors, status=400)return render(request, 'components/form.html', context={"form" : form})
also add LOGIN_URL in /Twittme/settings.py
LOGIN_URL = "/login"
to test, runserver and the terminal displays:
AnonymousUser
so the home page did not successfully send a status 401.
again, modify /tweets/views.py
def tweet_create_view(request, *args, **kwargs):user = request.user # newif not request.user.is_authenticated:user = None # newif request.is_ajax():return JsonResponse({}, status=401)return redirect(settings.LOGIN_URL)form = TweetForm(request.POST or None)print('post data is', request.POST)next_url = request.POST.get("next") or Noneif form.is_valid():obj = form.save(commit=False)obj.user = request.user # newobj.save()if request.is_ajax():return JsonResponse(obj.serialize(), status=201)if next_url != None and is_safe_url(next_url, ALLOWED_HOSTS):return redirect(next_url)form = TweetForm()if form.errors:if request.is_ajax():return JsonResponse(form.errors, status=400)
to test, runserver and send something when not login

now we have a 401 status.
and also if we login and send a tweet, it works as before.Permissions & Roadmap:
add how to handle status 401 in home.html
function handleTweetCreateFormDidSubmit(event) {//...xhr.onload = function() {if (xhr.status === 201) {//...} else if(xhr.status === 400){//...} else if(xhr.status === 401){ // newalert("You must login!") //new alertwindow.location.href = "/login" //redirect to /login} else if(xhr.status === 500){alert("There was a server error. Please try again later.")}}xhr.onerror = function(){alert("An error occured. Please try again later.")}xhr.send(myFormData)}
to test, runserver and send something while not login

get the alert, and
redirected to login(now we have no template to handle /login)we need a user account before any tweet methods
- add “User Permissions” tag in todo.md ```
- Tweets
-> User Permissions
-> Creating-> Text-> Image -> Media Storage Server-> Delete-> Retweeting-> Liking
//…
<a name="qzW9G"></a>#### Install Django Rest Framework:In this step, we will install Django Rest Framework to use it's intergrated methods to substitute some parts before.- homepage of Django Rest Framework: [https://www.django-rest-framework.org/](https://www.django-rest-framework.org/)- install it in pipenv```bash(reactjs) [root@localhost Twittme]# pipenv install djangorestframeworkInstalling djangorestframework…Adding djangorestframework to Pipfile's [packages]…✔ Installation SucceededPipfile.lock (866705) out of date, updating to (18de66)…Locking [dev-packages] dependencies…Building requirements...Resolving dependencies...✔ Success!Locking [packages] dependencies…Building requirements...Resolving dependencies...✔ Success!Updated Pipfile.lock (18de66)!Installing dependencies from Pipfile.lock (18de66)…🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
add new app in /Twittme/settings.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',# third-party'rest_framework',# internal'tweets',]
Django Forms to Django Rest Framework Serializer:
In this part, change django froms into serializer of django rest framework.
for code reuse, copy ‘MAX_TWEET_LENGTH’ in /tweets/forms.py to /Twittme/settings.py
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']LOGIN_URL = "/login"MAX_TWEET_LENGTH = 240
create serializers.py in /tweets ```python from django.conf import settings from rest_framework import serializers
MAX_TWEET_LENGTH = settings.MAX_TWEET_LENGTH # better also apply this on forms.py from .models import Tweet
class TweetSerializer(serializers.ModelSerializer): class Meta: model = Tweet fields = [‘content’]
def validate_content(self, value):if len(value) > MAX_TWEET_LENGTH: # if too longraise serializers.ValidationError("This tweet is too long")return value
- also update forms.py```pythonfrom django.conf import settings # newfrom django import formsfrom .models import Tweet# MAX_TWEET_LENGTH = 240MAX_TWEET_LENGTH = settings.MAX_TWEET_LENGTH # newclass TweetForm(forms.ModelForm):class Meta:model = Tweetfields = ['content']def clean_content(self):content = self.cleaned_data.get("content")if len(content) > MAX_TWEET_LENGTH:raise forms.ValidationError("This tweet is too long")if len(content) == 0:raise forms.ValidationError("Content cannot be blank")return content
- add a new create view in views.py
- also rename the old create view method ```python from .models import Tweet from .forms import TweetForm from .serializers import TweetSerializer # new
def tweet_create_view(request, args, *kwargs): serializer = TweetSerializer(data=request.POST or None) if serializer.is_valid(): obj = serializer.save(user=request.user) print(obj) return JsonResponse(serializer.data, status=201) return JsonResponse({}, status=400)
def tweet_create_view(request, args, *kwargs):
def tweet_create_view_pure_django(request, args, *kwargs):
# ...
here status '201' is used to answer POST request, a bit different from 200<br />to test, runserver and send something<br /><br />'obj' is shown as 'Tweet object (49)', corresponding to 'serialize' I just sent.<br />also 'likes' is not initialized, so that is shown as 'undefined' in the button right now.<a name="YIZDj"></a>#### Django Views to Django Rest Framework Views:(continue)- modify views.py```pythonfrom rest_framework.decorators import api_view # newfrom rest_framework.response import Response # new# ...@api_view(['POST']) # http method the client === POSTdef tweet_create_view(request, *args, **kwargs):serializer = TweetSerializer(data=request.POST)if serializer.is_valid(raise_exception=True): # send back what the error isserializer.save(user=request.user)return Response(serializer.data, status=201)return Response({}, status=400)# ...
to test, runserver
and this create view still works.
Then change list_view after create_view:
- again, add and rename in views.py ```python @api_view([‘GET’]) # http method the client === POST def tweet_list_view(request, args, *kwargs): qs = Tweet.objects.all() # grab all objects in Tweet serializer = TweetSerializer(qs, many=True) return Response(serializer.data)
def tweet_list_view(request, args, *kwargs):
def tweet_list_view_pure_django(request, args, *kwargs):
# ...
runserver and see [http://localhost:8000/tweets](http://localhost:8000/tweets)<br /><br />serializer.data looks like above.- to let the display become same as before, in home.html```javascriptfunction loadTweets(tweetsElement){//...xhr.onload = function() {const serverResponse = xhr.response//var listedItems = serverResponse.responsevar listedItems = serverResponse //new//...}xhr.send()}
to test, runserver
now there is no difference except undefined likes.
Finally, detail_view:
- add and rename in home.html
```python
@api_view([‘GET’]) # http method the client === GET
def tweet_detail_view(request, tweet_id, args, *kwargs):
qs = Tweet.objects.filter(id=tweet_id)
if not qs.exists():
obj = qs.first() serializer = TweetSerializer(obj) return Response(serializer.data, status=200)return Response({}, status=404)
def tweet_detail_view(request, tweet_id, args, *kwargs):
def tweet_detail_view_pure_django(request, tweet_id, args, *kwargs):
# ...
to test, runserver, and access such as [http://localhost:8000/tweets/1](http://localhost:8000/tweets/1)<br />and it's more detailed then json page before.<a name="NE944"></a>#### Permissions and Authentication Classes Decorators for DRF APIs:- modify views.py to add IsAuthenticated factor into the permission verification step```pythonfrom rest_framework.decorators import api_view, permission_classes # newfrom rest_framework.permissions import IsAuthenticated # new# ...@api_view(['POST']) # http method the client === POST@permission_classes([IsAuthenticated])def tweet_create_view(request, *args, **kwargs):serializer = TweetSerializer(data=request.POST)if serializer.is_valid(raise_exception=True): # send back what the error isserializer.save(user=request.user)return Response(serializer.data, status=201)return Response({}, status=400)# ...
to test, runserver and send something while not login
the status code this time is ‘403’ (not authorized)
a new handler in home.html
function handleTweetCreateFormDidSubmit(event) {//...xhr.onload = function() {if (xhr.status === 201) {//...} else if(xhr.status === 400){//...} else if(xhr.status === 401){alert("You must login!")window.location.href = "/login"} else if(xhr.status === 403){ // newalert("You must login!") //new alertwindow.location.href = "/login" //redirect to /login} else if(xhr.status === 500){alert("There was a server error. Please try again later.")}}xhr.onerror = function(){alert("An error occured. Please try again later.")}xhr.send(myFormData)}
and if the status code is 403, the page will be redirected to /login
also add session authentication in views.py
- about session authentication: https://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication

# ...from rest_framework.authentication import SessionAuthentication # newfrom rest_framework.decorators import api_view, permission_classes, authentication_classes # new# ...@api_view(['POST']) # http method the client === POST@authentication_classes([SessionAuthentication]) # new@permission_classes([IsAuthenticated])def tweet_create_view(request, *args, **kwargs):serializer = TweetSerializer(data=request.POST)if serializer.is_valid(raise_exception=True): # send back what the error isserializer.save(user=request.user)return Response(serializer.data, status=201)return Response({}, status=400)
set default classes of rest framework in /Twittme/settings.py
- https://www.django-rest-framework.org/api-guide/settings/
after this setup, http://localhost:8000/tweets only shows json data.REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'],'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',]}

