Log in and log out views. Model-Update-View pattern and dependent types Logout html view set

Django comes with a lot of built-in resources for the most common use cases of a Web application. The registration app is a very good example and a good thing about it is that the features can be used out-of-the-box.

With the Django registration app you can take advantages of the following features:

  • Login
  • Logout
  • Sign up
  • Password reset

In this tutorial we will focus in the Login and Logout features. For sign up and password reset, check the tutorials below:

Getting started

Before we start, make sure you have django.contrib.auth in your INSTALLED_APPS and the authentication middleware properly configured in the MIDDLEWARE_CLASSES settings.

Both come already configured when you start a new Django project using the command startproject . So if you did not remove the initial configurations you should be all set up.

In case you are starting a new project just to follow this tutorial, create a user using the command line just so we can test the login and logout pages.

$ python manage.py createsuperuser

In the end of this article I will provide the source code of the example with the minimal configuration.

Configure the URL routes

First import the django.contrib.auth.views module and add a URL route for the login and logout views:

from django.conf.urls import url from django.contrib import admin from django.contrib.auth import views as auth_views urlpatterns = [ url (r"^login/$" , auth_views . login , name = "login" ), url ( r"^logout/$" , auth_views . logout , name = "logout" ), url (r"^admin/" , admin . site . urls ), ]

Create a login template

By default, the django.contrib.auth.views.login view will try to render the registration/login.html template. So the basic configuration would be creating a folder named registration and place a login.html template inside.

Following a minimal login template:

(% extends "base.html" %) (% block title %)Login(% endblock %) (% block content %)

Login

(% csrf_token %) (( form.as_p ))
(%endblock%)

This simple example already validates username and password and authenticate correctly the user.

Customizing the login view

There are a few parameters you can pass to the login view to make it fit your project. For example, if you want to store your login template somewhere else than registration/login.html you can pass the template name as a parameter:

url (r"^login/$" , auth_views . login , ( "template_name" : "core/login.html" ), name = "login" ),

You can also pass a custom authentication form using the parameter authentication_form , incase you have implemented a custom user model.

Now, a very important configuration is done in the settings.py file, which is the URL Django will redirect the user after a successful authentication.

Inside the settings.py file add:

LOGIN_REDIRECT_URL = "home"

The value can be a hardcoded URL or a URL name. The default value for LOGIN_REDIRECT_URL is /accounts/profile/ .

It is also important to note that Django will try to redirect the user to the next GET param.

Setting up logout view

After acessing the django.contrib.auth.views.logout view, Django will render the registration/logged_out.html template. In a similar way as we did in the login view, you can pass a different template like so:

url (r"^logout/$" , auth_views . logout , ( "template_name" : "logged_out.html" ), name = "logout" ),

Usually I prefer to use the next_page parameter and redirect either to the homepage of my project or to the login page when it makes sense.

This example demonstrates how to automatically logout with default Spring security configuration.

To logout, we just need to access URL "/logout" with POST request.

In the POST /logout form, we also need to include the CSRF token, which is a protection against CSRF attack .

Let's see the example how to do that.

Java Config class

@Configuration @EnableWebSecurity @EnableWebMvc @ComponentScan public class AppConfig extends WebSecurityConfigurerAdapter ( protected void configure(HttpSecurity http) throws Exception ( http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin(); ) @Override public void configure(AuthenticationManagerBuilder builder) throws Exception ( builder.inMemoryAuthentication() .withUser("joe") .password("123") .roles("ADMIN"); ) @Bean public ViewResolver viewResolver() ( InternalResourceViewResolver viewResolver = new InternalResourceViewResolver (); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp");

Note that, in above configuration, we are also overriding configure(HttpSecurity http) to omit the default Basic Authentication (see the original method in WebSecurityConfigurerAdapter source code) and use form based Authentication. We are doing so because browsers cache the Basic Authentication information aggressively (after the first successful login) and there is no way to logout the user in the current session. In most of the examples, we will not be using Basic Authentication mechanism.

A controller

@Controller public class ExampleController ( @RequestMapping("/") public String handleRequest2(ModelMap map) ( map.addAttribute("time", LocalDateTime.now().toString()); return "my-page"; ) )

The JSP page

src/main/webapp/WEB-INF/views/my-page.jsp

Spring Security Example

Time: $(time)

To try examples, run embedded tomcat (configured in pom.xml of example project below):

Mvn tomcat7:run-war

Output

Initial access to URI "/" will redirect to "/login":

After submitting user name and password as we setup in our AppConfig class:

Clicking on "Logout" button:


Example Project

Dependencies and Technologies Used:

  • spring-security-web 4.2.3.RELEASE: spring-security-web.
  • spring-security-config 4.2.3.RELEASE: spring-security-config.
  • spring-webmvc 4.3.9.RELEASE: Spring Web MVC.
  • javax.servlet-api 3.1.0 Java Servlet API
  • JDK 1.8
  • Maven 3.3.9

Edit the file urls.py applications account:

from django.conf.urls import url from . import views urlpatterns = [ # previous login view # url(r"^login/$", views.user_login, name="login"),# login / logout urls url(r"^login/$" , "django.contrib.auth.views.login", name="login" ), url(r"^logout/$" , "django.contrib.auth.views.logout", name="logout" ), url(r"^logout-then-login/$" , "django.contrib.auth.views.logout_then_login", name="logout_then_login" ), ]

We have commented out the URL template for the view user_login, created earlier to use the view login Django.

Create a new directory in the application templates directory account and name it registration. Create a new file in a new directory, name it login.html

(% extends "base.html" %) (% block title %)Log-in(% endblock %) (% block content %)

Log-in

(% if form.errors %)

Your username and password didn't match. Please try again.

(%else%)

Please, use the following form to log-in:

(%endif%) (%endblock%)

This login template is very similar to the one created earlier. Django uses AuthenticationForm, located in django.contrib.auth.forms. This form attempts to authenticate the user and generates a validation error if the username was incorrect. In this case, we can look for errors using the command (% if form.errors %) . Please note that we have added a hidden element to send the value of a variable named next.

Parameter next must be a URL. If this parameter is specified, then after the user logs in, they are redirected to the specified URL.

Now create a template logged_out.html inside the template directory registration and paste the following code into it:

(% extends "base.html" %) (% block title %)Logged out(% endblock %) (% block content %)

Logged out

You have been successfully logged out. You can log-in again.

(%endblock%)

This is the template that will be displayed after the user logs in.

After adding the URL templates and templates for the input and output views, the site is ready for logins using Django's authentication views.

Please note that the presentation logout_then_login included in our urlconf, doesn't need a template since it redirects to log in view.

Now let's create a new view to display a dashboard for the user so that we know when the user logs into his account. Open the file views.py applications account and add the following code to it:

from django.contrib.auth.decorators import login_required @login_required def dashboard(request) : return render(request, "account/dashboard.html" , ("section" : "dashboard" ))

We add a decorator to our view login_required authentication framework. Decorator login_required checks whether the current user is authenticated. If the user is authenticated, the submission will be executed; If the user is not authenticated, they will be redirected to the login page.

We also defined a variable section. We're going to use this variable to track which section of the site the user is watching.

Now you need to create a template for the dashboard view. Create a new file inside templates/account templates/account/ and name it dashboard.html :

(% extends "base.html" %) (% block title %)Dashboard(% endblock %) (% block content %)

Dashboard

Welcome to your dashboard.

(%endblock%)

Then add the following URL pattern for this change file urls.py applications account:

Urlpatterns = [ # ... url(r"^$" , views.dashboard, name="dashboard" ), ]

Now edit the file settings.py:

from django.core.urlresolvers import reverse_lazy LOGIN_REDIRECT_URL = reverse_lazy("dashboard" ) LOGIN_URL = reverse_lazy("login" ) LOGOUT_URL = reverse_lazy("logout" )
  • LOGIN_REDIRECT_URL: Tells which URL to redirect the user to after logging in.
  • LOGIN_URL: URL to redirect the user to login (for example, using a decorator login_required)
  • LOGOUT_URL: URL to redirect the user to exit

Now we're going to add login and logout links to our basic template.

To do this, it is necessary to determine whether the current user is logged in or not in order to display a link corresponding to the current user state. The current user is specified in HttpRequest object of the authentication intermediate class. It can be accessed using request.user. The request will find a user object, even if the user is not authenticated. Unauthenticated user, specified in the request as an instance AnonymousUser. The best way to check the current user's authentication status is to call request.user.is_authenticated()

Edit in the template base.html

with ID header:

As you can see, the site menu is displayed only for authenticated users. We also check the current section to add the selected class attribute to the corresponding element

  • to highlight the current section in the menu using CSS. It also displays the username and a link to log out if the user is authenticated, or a link to log in.

    Open http://127.0.0.1:8000/account/login/ in your browser. You should see a login page. Enter a valid username and password. You will see the following:

    You can see that the My dashboard section is highlighted with CSS as it has a class selected. Since the user has been authenticated, the username is displayed on the right side of the header. Click the link Logout. You will see the following page:

    On this page you can see that the user is logged out and therefore the website menu is no longer displayed. The link on the right side of the header now shows Log-in.

    If you see the log out page from the Django admin site rather than your own logout page, check your INSTALLED_APPS settings and make sure that django.contrib.admin is after account. Both templates are in the same relative path, and the Django template loader will use the first one it finds.

    Mainly for developing user interfaces. To use it, you need to create a Model type that represents the complete state of the program, a Message type that describes external environmental events to which the program must respond by changing its state, an updater function that creates a new state of the program from the old state and message, and a view function that Based on the state of the program, it calculates the required impacts on the external environment, which generate events of the Message type. The pattern is very convenient, but it has a small drawback - it does not allow you to describe which events make sense for specific program states.

    A similar problem arises (and is solved) when using the State OO pattern.

    The Elm language is simple, but very strict - it checks that the updater function somehow handles all possible combinations of model-state and message-events. Therefore, you have to write extra, albeit trivial, code that usually leaves the model unchanged. I want to demonstrate how this can be avoided in more complex languages ​​- Idris, Scala, C++ and Haskell.

    All code shown here is available on GitHub for experimentation. Let's look at the most interesting places.


    The msg function is unusual - it returns a type, not a value. During execution, nothing is known about value types - the compiler erases all unnecessary information. That is, such a function can only be called at the compilation stage.

    MUV is a constructor. It accepts parameters: model - the initial state of the program, updater - a function for updating the state upon an external event, and view - a function for creating an external view. Note that the type of the updater and view functions depends on the value of the model (using the msg function from the type parameters).

    Now let's see how to launch this application

    MuvRun: (Application modelType msgType IO) -> IO a muvRun (MUV model updater view) = do msg<- view model muvRun (MUV (updater model msg) updater view)
    We chose an input/output operation as an external representation (view) (in Idris, as in Haskell, input/output operations are first class values; in order for them to be executed, additional actions must be taken, usually returning such an operation from the main function).

    Briefly about IO

    When performing an operation of type (IO a), some impact on the outside world occurs, possibly empty, and a value of type a is returned to the program, but the functions of the standard library are designed in such a way that it can only be processed by generating a new value of type IO b. This way pure functions are separated from functions with side effects. This is unusual for many programmers, but it helps to write more reliable code.


    Since the muvRun function generates I/O, it should return IO, but since it will never complete, the operation type can be anything - IO a.

    Now let's describe the types of entities we are going to work with

    Data Model = Logouted | Logined String data MsgOuted = Login String data MsgIned = Logout | Greet total msgType: Model -> Type msgType Logouted = MsgOuted msgType (Logined _) = MsgIned
    Here we describe a model type that reflects the presence of two interface states - the user is not logged in, and the user with a name of type String is logged in.

    Next we describe two different types of messages relevant for different variants of the model - if we are logged out, then we can only log in under a certain name, and if we are already logged in, we can either log out or say hello. Idris is a strongly typed language that will not allow for the possibility of mixing up different types.

    And finally, a function that sets the correspondence of the model value to the message type.

    The function is declared total - that is, it should not crash or freeze, the compiler will try to monitor this. msgType is called at compilation time, and its totality means that the compilation will not freeze due to our error, although it cannot guarantee that executing this function will exhaust system resources.
    It is also guaranteed that it will not execute "rm -rf /" because there is no IO in its signature.

    Let's describe updater:

    Total updater: (m:Model) -> (msgType m) -> Model updater Logouted (Login name) = Logined name updater (Logined name) Logout = Logouted updater (Logined name) Greet = Logined name
    I think the logic of this function is clear. I would like to note once again totality - it means that the Idris compiler will check that we have considered all the alternatives allowed by the type system. Elm also performs this check, but it cannot know that we cannot log out if we are not logged in yet, and will require explicit processing of the condition

    Updater Logouted Logout = ???
    Idris will find type mismatches in an unnecessary check.

    Now let's move on to the view - as usual in UI, this will be the most difficult part of the code.

    Total loginPage: IO MsgOuted loginPage = do putStr "Login: " map Login getLine total genMsg: String -> MsgIned genMsg "" = Logout genMsg _ = Greet total workPage: String -> IO MsgIned workPage name = do putStr ("Hello, " ++ name ++ "\n") putStr "Input empty string for logout or nonempty for greeting\n" map genMsg getLine total view: (m: Model) -> IO (msgType m) view Logouted = loginPage view (Logined name ) = workPage name
    view must create an I/O operation that returns messages, the type of which again depends on the value of the model. We have two options: loginPage, which prints a "Login:" message, reads a string from the keyboard and wraps it in a Login message, and workPage with a username parameter, which prints a welcome message and returns different messages (but the same type - MsgIned) depending on whether the user enters an empty or non-empty string. view returns one of these operations depending on the value of the model, and the compiler checks their type, even though it is different.

    Now we can create and run our application

    App: Application Model Main.msgType IO app = MUV Logouted updater view main: IO () main = muvRun app
    A subtle point should be noted here - the muvRun function returns IO a, where a was not specified and the value main is of type IO(), Where () is the name of a type usually called Unit, which has a single value, also written as an empty tuple () . But the compiler can handle this easily. substituting a() instead.

    Scala and path-dependent types

    Scala does not have full support for dependent types, but there are types that depend on the instance of the object through which it is referenced (path dependent types). In the theory of dependent types, they can be described as a variant of the sigma type. Path-dependent types make it possible to prohibit the addition of vectors from different vector spaces, or to describe who can kiss whom. But we will use them for simpler tasks.

    Sealed abstract class MsgLogouted case class Login(name: String) extends MsgLogouted sealed abstract class MsgLogined case class Logout() extends MsgLogined case class Greet() extends MsgLogined abstract class View ( def run() : Msg ) sealed abstract class Model ( type Message def view() : View ) case class Logouted() extends Model ( type Message = MsgLogined override def view() : View .... ) case class Logined(name: String) extends Model ( type Message = MsgLogined override def view( ): View .... )
    Algebraic types in Scala are modeled through inheritance. The type corresponds to some sealed abstract class, and each constructor inherited from it case class. We will try to use them exactly as algebraic types, describing all variables as belonging to the parent sealed abstract class.

    The MsgLogined and MsgLogouted classes within our program do not have a common ancestor. The view function had to be spread across different classes of the model in order to have access to a specific type of message. This has its advantages, which OO supporters will appreciate - the code is grouped in accordance with business logic, everything related to one use case is nearby. But I would rather separate view into a separate function, the development of which could be transferred to another person.

    Now let's implement updater

    Object Updater ( def update(model: Model)(msg: model.Message) : Model = ( model match ( case Logouted() => msg match ( case Login(name) => Logined(name) ) case Logined(name) => msg match ( case Logout() => Logouted() case Greet() => model ) ) ) )
    Here we use path-dependent types to describe the type of the second argument from the value of the first. In order for Scala to accept such dependencies, functions have to be described in curried form, that is, as a function from the first argument, which returns a function from the second argument. Unfortunately, Scala doesn't do many type checks at this point for which the compiler has enough information.

    Now let's give a complete implementation of the model and view

    Case class Logouted() extends Model ( type Message = MsgLogouted override def view() : View = new View ( override def run() = ( println("Enter name ") val name = scala.io.StdIn.readLine() Login (name) ) ) case class Logined(name: String) extends Model ( type Message = MsgLogined override def view() : View = new View ( override def run() = ( println(s"Hello, $name") println ("Empty string for logout, nonempy for greeting.") scala.io.StdIn.readLine() match ( case "" => Logout() case _ => Greet() ) ) ) abstract class View ( def run( ) : Msg ) object Viewer ( def view(model: Model): View = ( model.view() ) )
    The return type of a view function depends on the instance of its argument. But for implementation it turns to the model.

    The application created in this way is launched like this:

    Object Main ( import scala.annotation.tailrec @tailrec def process(m: Model) ( val msg = Viewer.view(m).run() process(Updater.update(m)(msg)) ) def main(args: Array) = ( process(Logouted()) ) )
    The runtime system code thus does not know anything about the internal structure of models and message types, but the compiler can check that the message matches the current model.

    Here we did not need all the capabilities provided by path-dependent types. Interesting properties will appear if we work in parallel with several instances of Model-Updater-View systems, for example, when simulating a multi-agent world (the view would then represent the agent’s influence on the world and receiving feedback). In this case, the compiler checked that the message was processed by exactly the agent for which it was intended, despite the fact that all agents have the same type.

    C++

    C++ is still sensitive to the order of definitions, even if they are all made in the same file. This creates some inconvenience. I will present the code in a sequence convenient for demonstrating ideas. A compilable version can be found on GitHub.

    Algebraic types can be implemented in the same way as in Scala - an abstract class corresponds to a type, and concrete descendants correspond to constructors (let's call them “constructor classes”, so as not to be confused with ordinary C++ constructors) of the algebraic type.

    C++ has support for path-dependent types, but the compiler cannot use the type in the abstract without knowing the real type it is associated with. Therefore, it is impossible to implement Model-Updater-View with their help.

    But C++ has a powerful templating system. The dependence of the type on the model value can be hidden in a template parameter of a specialized version of the executive system.

    Struct Processor ( virtual const Processor *next() const = 0; ); template struct ProcessorImpl: public Processor ( const CurModel * model; ProcessorImpl (const CurModel* m) : model(m) ( ); const Processor *next() const ( const View
    We describe an abstract execution system, with a single method to do whatever is required and return a new execution system suitable for the next iteration. The specific version has a template parameter and will be specialized for each “constructor class” of the model. It is important here that all properties of the CurModel type will be checked during the specialization of the template with a specific type parameter, and at the time of compilation of the template itself, they are not required to be described (although it is possible using concepts or other ways of implementing type classes). Scala also has a fairly powerful system of parameterized types, but it checks the properties of parameter types during compilation of the parameterized type. There, the implementation of such a pattern is difficult, but possible, thanks to the support of type classes.

    Let's describe the model.

    Struct Model ( virtual ~Model() (); virtual const Processor *processor() const = 0; ); struct Logined: public Model ( struct Message ( const virtual Model * process(const Logined * m) const = 0; virtual ~Message() (); struct Logout: public Message ( const Model * process(const Logined * m) const; struct Greet: public Message ( const Model * process(const Logined * m) const; ); const std::string name; public View (...); const View * view() const ( return new LoginedView(name); ); (...); const Processor *processor() const ( return new ProcessorImpl (this);
    ); ); struct Logouted: public Model ( struct Message ( const virtual Model * process(const Logouted * m) const = 0; virtual ~Message() (); struct Login: public Message ( const std::string name; Login(std ::string lname) : name(lname) ( ); const Model * process(const Logouted * m) const ;

    * view() const ( return new LogoutedView(); );

    Const Model * Logouted::Login::process(const Logouted * m) const ( delete m; return new Logined(name); ); const Model * Logined::Logout::process(const Logined * m) const ( delete m; return new Logouted(); ); const Model * Logined::Greet::process(const Logined * m) const ( return m; );
    Now let's put together everything that relates to the view, including the internal entities of the models

    Template struct View ( virtual const Message * run() const = 0; virtual ~View () (); ); struct Logined: public Model ( struct LoginedView: public View ( const std::string name; LoginedView(std::string lname) : name(lname) (); virtual const Message * run() const ( char buf; printf("Hello %s", name.c_str()) ; fgets(buf, 15, stdin); return (*buf == 0 || *buf == "\n" || *buf == "\r") ? (new Logout()) : static_cast (new Greet); ); ); const View
    * view() const ( return new LoginedView(name); ); ); struct Logouted: public Model ( struct LogoutedView: public View

    ( virtual const Message * run() const ( char buf; printf("Login: "); fgets(buf, 15, stdin); return new Login(buf); ); ); const View

    * view() const ( return new LogoutedView(); ); );

    And finally, let's write main

    Int main(int argc, char ** argv) ( const Processor * p = new ProcessorImpl

    (new Logouted());


    while(true) ( ​​const Processor * pnew = p->next(); delete p; p = pnew; ) return 0; )

    Class ProcessorImpl(model: M)(implicit updater: (M, Message) => Model, view: M => View) extends Processor ( def next(): Processor = ( val v = view(model) val msg = v. run() val newModel = updater(model,msg) newModel.processor() ) )
    Here we see new mysterious parameters (implicit updater: (M, Message) => Model, view: M => View). The implicit keyword means that when calling this function (more precisely, the class constructor), the compiler will look for objects of suitable types marked as implicit in the context and pass them as appropriate parameters. This is a fairly complex concept, one of whose applications is the implementation of type classes. Here they promise the compiler that for specific implementations of the model and message, all the necessary functions will be provided by us. Now let's fulfill this promise.

    Object updaters ( implicit def logoutedUpdater(model: Logouted, msg: LogoutedMessage): Model = ( (model, msg) match ( case (Logouted(), Login(name)) => Logined(name) ) ) implicit def viewLogouted(model : Logouted) = new View ( override def run() : LogoutedMessage = ( println("Enter name ") val name = scala.io.StdIn.readLine() Login(name) ) ) implicit def loginedUpdater(model: Logined, msg : LoginedMessage): Model = ( (model, msg) match ( case (Logined(name), Logout()) => Logouted() case (Logined(name), Greet()) => model ) ) implicit def viewLogined( model: Logined) = new View ( val name = model.name override def run() : LoginedMessage = ( println(s"Hello, $name") println("Empty string for logout, nonempy for greeting.") scala.io .StdIn.readLine() match ( case "" => Logout() case _ => Greet() ) ) ) import updaters._

    Haskell

    There are no dependent types in mainstream Haskell. It also lacks inheritance, which we significantly used when implementing the pattern in Scala and C++. But single-level inheritance (with elements of dependent types) can be modeled using more or less standard language extensions - TypeFamilies and ExistentialQuantification. For the common interface of child OOP classes, a type class is created, in which there is a dependent “family” type, the child classes themselves are represented by a separate type, and then wrapped in an “existential” type with a single constructor.

    Data Model = forall m. (Updatable m, Viewable m) => Model m class Updatable m where data Message m:: * update:: m -> (Message m) -> Model class (Updatable m) => Viewable m where view:: m -> (View (Message m)) data Logouted = Logouted data Logined = Logined String
    I tried to separate updater and view as far as possible, so I created two different type classes, but so far it hasn’t worked out well.

    The updater implementation is simple

    Instance Updatable Logouted where data Message Logouted = Login String update Logouted (Login name) = Model (Logined name) instance Updatable Logined where data Message Logined = Logout | Greeting update m Logout = Model Logouted update m Greeting = Model m
    I had to fix IO as View. Attempts to make it more abstract greatly complicated everything and increased the coupling of the code - the Model type must know which View we are going to use.

    Import System.IO type View a = IO a instance Viewable Logouted where view Logouted = do putStr "Login: " hFlush stdout fmap Login getLine instance Viewable Logined where view (Logined name) = do putStr $ "Hello " ++ name ++ " !\n" hFlush stdout l<- getLine pure $ if l == "" then Logout else Greeting
    Well, the executable environment differs little from the similar one in Idris

    RunMUV::Model -> IO a runMUV (Model m) = do msg<- view m runMUV $ update m msg main:: IO () main = runMUV (Model Logouted)

  • mob_info