DAY-4 Authentication, Forms and SEO

1. Authentication

Django comes with a module that handles authentication. Let’s try to use that module so that your users can sign up for your event website. The module already contains basic features like register, login, logout and forgetpassword - so let’s integrate them.

1.1 Signup

Let your users signup for your page.

git checkout -f day-4-step-1

Changes:

  • We have added a register() function in events/view.py

    At the top we need to import ...

    from django.contrib.auth.forms import UserCreationForm
    from django.http import HttpResponseRedirect
    

    And then we have added the register() function

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    def register(request):
       if request.method == 'POST':
           form = UserCreationForm(request.POST)
           if form.is_valid():
               new_user = form.save()
               return HttpResponseRedirect("/")
       else:
           form = UserCreationForm()
       return render(request, "users/register.html", {
           'form': form,
       })
    
  • We have modified template/html/base.html and events/templates/events/home.html and added Login and Register links in the top right corner.

  • We have added a new template file event/tempalte/users/register.html:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    {% extends "base.html" %}
    
    {% block 'content' %}
      <h1>Create an account</h1>
    
      <form action="" method="post">
          {{ form.as_p }}
          <input type="submit" value="Create the account">
          {% csrf_token %}
      </form>
    {% endblock %}
    

    Note

    Here, we we have used {% crsf_token %}. This provides protection against the so called CSRF attack (“Cross-site request forgery”). It is a security protection against very common attack vectors.

  • We also added an url pattern, in myevent/urls.py:

    url(r'^register/$', 'events.views.register', name='register')
    

► Run project and point your browser to /register. You will get a register form with minimum fields - username, password and confirm password. Try to register a new user.

1.2 Login

No that you have registered a new user - you should be able to login. Similar to registration, adding a login function is easy. Checkout the new code:

git checkout -f day-4-step-2

Changes:

  • We have added a new file events/template/users/login.html.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
     {% extends "base.html" %}
    
     {% block 'content' %}
       <h1>Login</h1>
    
       <form action="" method="post">
           {{ form.as_p }}
           <input type="submit" value="Login">
           {% csrf_token %}
       </form>
     {% endblock %}
    
  • We have added lines in myevent/urls.py:

    Import views from Auth module because we’ll be using a default view from Django core (Auth Module).

    from django.contrib.auth import views as auth_view
    

    Add URL pattern:

    url(r'^login/$', auth_view.login, name='login', kwargs={'template_name': 'users/login.html'}),
    

    Note

    This is different from the rest of other url patterns. With the second parameter auth_view.login we instruct Django to use the inbuilt login method. With kwargs, we point to our custom form template.

  • We have also added the following two lines at myevent/settings.py.

    LOGIN_URL = "/login"
    LOGIN_REDIRECT_URL = "/"
    

► Run project and click on /login.

  1. Enter a username and password.
  2. In case of a successful login, you will be taken to the home page.
  3. In case of failure, an error message will be shown.

1.4 Welcome the User

Did you notice that when your user logged in, nothing seemed to happen. Let’s add a welcome <username> message for logged in user.

git checkout -f day-4-step-3

Changes:

  • We have changed the following parts of code in events/template/events/home.html and template/html/base.html.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    <div class="navbar-collapse collapse">
       <ul class="nav navbar-nav">
           {% if request.user.is_authenticated %}
           <li><a href="#">Welcome {{request.user.username}}</a></li>
           {% else %}
           <li><a href="/login/">Sign In</a></li>
           <li><a href="/register/">Register</a></li>
           {% endif %}
       </ul>
    </div>
    

Here, we have added a condition to show “welcome <username>” if the user is authenticated. In line 3, you can see {% if request.user.is_authenticated %} is the code to check whether the user is authenticated or not. Otherwise, we’ll show a link for “Sign In” and “Registration”.

► Run project and check your site. If you are logged in you will see WELCOME {USERNAME}.

1.3 Logout

Ok - finally let’s add the logout feature.

git checkout -f day-4-step-4

Changes:

  • We have added the following lines in myevent/urls.py:

    url(r'^logout/$', auth_view.logout, name='logout', kwargs={'next_page': '/'}),
    

    Note

    Same as login, for logout we are using a default function from the Auth module. In kwargs, we have added different variable kwargs={'next_page': '/'}. This will instruct django to redirect the user to the home page (/) after logout.

  • Added logout link in events/template/events/home.html and template/html/base.html

    <li><a href="/logout/">Log out</a></li>
    

► Run project and click on Log out. In case you are not logged in, you will see SIGN IN

2. Who is attending an Event?

Ok now we want to add some more complex functionality. We are going to add a RSVP (going or not going) feature for our Event site.

2.1 Event RSVP

We want the user to say if he is going to attend to an event (join) or not. We also want that he can change his decision.

git checkout -f day-4-step-5

Changes:

  • The following changes are made in event/views.py:

    At the top, we import the login_required decorator.

    from django.contrib.auth.decorators import login_required
    from django.core.urlresolvers import reverse
    

    Below, we have added the join() function

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @login_required
    def join(request, event_id):
        try:
            # already joined
            event = Event.objects.get(id=event_id, guest=request.user)
            message = "You have already joined this event"
        except Event.DoesNotExist as e:
            # Event exist but joined
            try:
                event = Event.objects.get(id=event_id)
                event.guest.add(request.user)
                event.save()
                message = "You have joined this event"
            except Event.DoesNotExist as e:
                message = "Error on event joining"
    
        event = Event.objects.get(id=event_id)
        return render(request, 'events/detail.html', {'event': event, 'message': message})
    
    1. Line 1: @login_requried is special decorator(=wrapper function) that only authenticated users can access this function.
    2. Line 5: It queries whether the user has already joined an event. If the user has not joined yet, it will throw a DoesNotExis exception.
    3. Line 10: It queries if there exists event with given id.
    4. Line 11: If event exists, it will add current user which means user joined
    5. Line 12: Saves the changes.
    6. Line 17: Get the current event object
    7. Line 18: Pass event object and message to detail.html template and reders.

    The goal of this function is to join the user if he, she has not already joined the event.

  • We have also added guest field in events/model.py.

    At the top, we have imported the User model (default from Django).

    from django.contrib.auth.models import User
    

    In the Event class we have added a Guest attribute

    class Event(models.Model):
        title = models.CharField(max_length=200)
        description = models.TextField(default='')
        venue = models.CharField(max_length=100)
        seats = models.IntegerField()
        amount = models.DecimalField(
            max_digits=11,
            decimal_places=2
        )
        date = models.DateTimeField()
        duration = models.CharField(
            max_length=20,
            help_text='e.g. 3 hours, 5 days'
        )
    
        guest = models.ManyToManyField(User, blank=True)
    
        def __str__(self):
            return "%s" % self.title
    

    Since we have made changes to our model - we need to migrate the database.

    Note

    This will generate a Many-To-Many Relation to the Django User table. An Event can have Multiple Users. A User can attend multiple Events.

    python manage.py migrate
    
  • We have added two lines to events/template/events/detail.html:

    <p clss="msg">{% if request.GET.msg %} {{request.GET.msg }} {% endif %}</p>
    <a href="{% url 'event_join' event.id %}" class="btn join">Join Event</a>
    
    1. First line will print msg if there’s message in query string.
    2. Second line is a link where the user can click to join the event.
  • We have added the following line in /myevent/urls.py

    url(r'^events/join/(?P<event_id>\d+)/$', 'events.views.join', name='event_join'),
    

► Run project. Point your browser to /events/ and click on any event. You’ll see a Join Event button.

2.2 Revoke Joining an Event

Sometimes a users change their plans. They should be able to cancel their request to join an event. Let us add a function for this.

git checkout -f day-4-step-6

Changes:

  • We have added a cancel() function in events/views.py:

    join() function is modified to:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @login_required
    def join(request, event_id):
        try:
            # already joined
            event = Event.objects.get(id=event_id, guest=request.user)
            message = "You have already joined this event"
        except Event.DoesNotExist as e:
            # Event exist but joined
            try:
                event = Event.objects.get(id=event_id)
                event.guest.add(request.user)
                event.save()
                message = "You have joined this event"
            except Event.DoesNotExist as e:
                message = "Error on event joining"
    
        event = Event.objects.get(id=event_id)
        joined = event.guest.filter(id=request.user.id)
        return render(request, 'events/detail.html', {
           'event': event,
           'message': message,
           'joined': joined
        })
    

    Line 18 added, and in line 19-22 joined variable is passed

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    @login_required
    def cancel(request, event_id):
        try:
            event = Event.objects.get(id=event_id, guest=request.user)
            event.guest.remove(request.user)
            event.save()
            message = "Your request not to attend has been saved"
        except Event.DoesNotExist as e:
               message = "Error on cancelling your attedance on event"
    
        event = Event.objects.get(id=event_id)
        joined = event.guest.filter(id=request.user.id)
        return render(request, 'events/detail.html', {
            'event': event,
            'message': message,
            'joined': joined
        })
    
  1. Line 1: @login_requried is special decorator that only authenticated users can access this function.
  2. Line 4: It will query events based on the event_id and user. If a user has not joined an event, it will throw a relation-DoesNotExist exception.
  3. Line 5: It will remove particular user from the event.
  • We have made some changes in the detail() function in /events/views.py

    1
    2
    3
    4
    def detail(request, id):
       event = Event.objects.get(id=id)
       joined = event.guest.filter(id=request.user.id)
       return render(request, 'events/detail.html', {'event': event, 'joined': joined})
    
    1. Line 3: new line added to query if current user has joined the event.
    2. Line 4: We added a flag, 'joined': joined, to the template. If user has not joined, this will be a false.
  • The following line has been changed in events/template/events/detail.html:

    1
    2
    3
    4
    5
    {% if not joined %}
    <a href="{% url 'event_join' event.id %}" class="btn join">Yes, Attend</a>
    {% else %}
    <a href="{% url 'event_cancel' event.id %}" class="btn join">No, Not Attend</a>
    {% endif %}
    
    1. Line 2: if joined flag is true (user already joined event), it will show a cancel button.
    2. Line 4: Nothing has changed in this line. We just made this line conditional - it is visible to those users who have not joined the event.
  • We have added the following line in myevent/urls.py file.

    url(r'^events/cancel/(?P<event_id>\d+)/$', 'events.views.cancel', name='event_cancel'),
    

► Run project. Join an event (if not already joined), you will see No, Not Attend button.

2.3 Who is going to an Event

Now we want to know what are the hottest events - and where do my friends go on the weekend. Let’s add a function that shows which users are attending to which event.

git checkout -f day-4-step-7

Changes:

  • Following lines are added in events/template/events/view.html:
1
2
3
4
5
6
7
<div class="user">
    <ul>
        {% for guest in event.guest.all %}
        <li> {{ guest.username }} </li>
        {% endfor %}
    </ul>
</div>
  1. Line 3: Loop over all guests who have joined this evevent.
  2. Line 4: Print username of user.

► Reload your browser.. You will see a list of users who are attending an event.

3 Events that you are attending

Let’s make a page that shows all events that the logged in user is attending.

git checkout -f day-4-step-8

Changes:

  • Following changes have been made in events/views.py.

    At top, we have imported User model:

    from django.contrib.auth.models import User
    

    Below, we have added the user_event() function

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def user_event(request, user_id):
       try:
           event_list = Event.objects.filter(guest__id=user_id)
           user = User.objects.get(id=user_id)
       except:
           event_list = []
           user = {}
    
       return render(request, 'events/user_event.html', {'event_list': event_list, 'user': user})
    
  • We have added a new file events/template/events/user_event.html which is a copy of events/template/events/list.html with a few changes:

 <section id="schedule" class="row">
 {% if not event_list %}
 There is no event.
 {% else %}
 <div class="title-start schedule-menu col-md-4 col-md-offset-4">
 <br />
     <h2>{{user.username}} is going in: </h2>
 </div>

Here we have added a condition to print "There is no event" given that the user has no event. We have also made changes in :code:`<h2>{{user.username}} is going in: </h2>`.
  • We have added My Events menu in template/html/base.html and events/template/events/home.html.

    <li><a href="{% url 'user_event' request.user.id %}">My Events</a></li>
    
  • Following line has been added in myevent/urls.py:

url(r'^user_event/(?P<user_id>\d+)/$', 'events.views.user_event', name='user_event')

4. Excercises

  1. Add AdThis social sharing widget in user_event

    Here is code, put it at below </ul> tag in events/templates/events/user_event.html file.

    <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-55ae89800d295a5e" async="async"></script>
    <div class="addthis_native_toolbox"></div>
    
  2. Share on facebook