CBV - RedirectView

From Classy Class Based View the RedirectView will

Provide a redirect on any GET request.

It is an extension of View and has 5 attributes:

  • http_method_names (from View)
  • pattern_name: The name of the URL pattern to redirect to. 1 This will be used if no url is used.
  • permanent: a flag to determine if the redirect is permanent or not. If set to True, then the HTTP Status Code 301 is returned. If set to False the 302 is returned
  • query_string: If True then it will pass along the query string from the RedirectView. If it’s False it won’t. If this is set to True and neither pattern\_name nor url are set then nothing will be passed to the RedirectView
  • url: Where the Redirect should point. It will take precedence over the patter_name so you should only url or patter\_name but not both. This will need to be an absolute url, not a relative one, otherwise you may get a 404 error

The example below will give a 301 status code:

class myRedirectView(RedirectView):
    pattern_name = 'rango:template_view'
    permanent = True
    query_string = True

While this would be a 302 status code:

class myRedirectView(RedirectView):
    pattern_name = 'rango:template_view'
    permanent = False
    query_string = True

Methods

The method get\_redirect\_url allows you to perform actions when the redirect is called. From the Django Docs the example given is increasing a counter on an Article Read value.

Diagram

A visual representation of how RedirectView derives from View 2

Redirect View

Conclusion

In general, given the power of the url mapping in Django I’m not sure why you would need to use a the Redirect View. From Real Python they concur, stating:

As you can see, the class-based approach does not provide any obvious benefit while adding some hidden complexity. That raises the question: when should you use RedirectView?

If you want to add a redirect directly in your urls.py, using RedirectView makes sense. But if you find yourself overwriting getredirecturl, a function-based view might be easier to understand and more flexible for future enhancements.

  1. From the [Django Docs](https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/
  2. Original Source from Classy Class Based Views

CBV - Template View

From Classy Class Based Views the TemplateView will

Render a template. Pass keyword arguments from the URLconf to the context.

It is an extended version of the View CBV with the the ContextMixin and the TemplateResponseMixin added to it.

It has several attributes that can be set

  • content_type: will allow you to define the MIME type that the page will return. The default is DEFAULT\_CONTENT\_TYPE but can be overridden with this attribute.
  • extra_context: this can be used as a keyword argument in the as\_view() but not in the class of the CBV. Adding it there will do nothing
  • http_method_name: derived from View and has the same definition
  • response_classes: The response class to be returned by render_to_response method it defaults to a TemplateResponse. See below for further discussion
  • template_engine: can be used to specify which template engine to use IF you have configured the use of multiple template engines in your settings.py file. See the Usage section of the Django Documentation on Templates
  • template_name: this attribute is required IF the method get\_template\_names() is not used.

More on response_class

This confuses the ever living crap out of me. The best (only) explanation I have found is by GitHub user spapas in his article Django non-HTML responses:

From the previous discussion we can conclude that if your non-HTML response needs a template then you just need to create a subclass of TemplateResponse and assign it to the responseclass attribute (and also change the contenttype attribute). On the other hand, if your non-HTML respond does not need a template to be rendered then you have to override rendertoresponse completely (since the template parameter does not need to be passed now) and either define a subclass of HttpResponse or do the rendering in the rendertoresponse.

Basically, if you ever want to use a non-HTML template you’d set this attribute, but it seems available mostly as a ‘just-in-case’ and not something that’s used every day.

My advise … just leave it as is.

When to use the get method

An answer which makes sense to me that I found on StackOverflow was (slightly modified to make it more understandable)

if you need to have data available every time, use get_context_data(). If you need the data only for a specific request method (eg. in get), then put it in get.

When to use the get_template_name method

This method allows you to easily change a template being used based on values passed through GET.

This can be helpful if you want to have one template for a super user and another template for a basic user. This helps to keep business logic out of the template and in the view where it belongs.

This can also be useful if you want to specify several possible templates to use. A list is passed and Django will work through that list from the first element to the last until it finds a template that exists and render it.

If you don’t specify template_name you have to use this method.

When to use the get_context_data method

See above in the section When to use the get method

Diagram

A visual representation of how TemplateView derives from View 1

TemplateView

Conclusion

If you want to roll your own CBV because you have a super specific use case, starting at the TemplateView is going to be a good place to start. However, you may find that there is already a view that is going to do what you need it to. Writing your own custom implementation of TemplateView may be a waste of time IF you haven’t already verified that what you need isn’t already there.

  1. Original Source from Classy Class Based Views

CBV - View

View is the ancestor of ALL Django CBV. From the great site Classy Class Based Views, they are described as

Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking.

This is no joke. The View class has almost nothing to it, but it’s a solid foundation for everything else that will be done.

Its implementation has just one attribute http_method_names which is a list that allows you to specify what http verbs are allowed.

Other than that, there’s really not much to it. You just write a simple method, something like this:

def get(self, _):
    return HttpResponse('My Content')

All that gets returned to the page is a simple HTML. You can specify the content_type if you just want to return JSON or plain text but defining the content_type like this:

def get(self, _):
    return HttpResponse('My Content', content_type='text plain')

You can also make the text that is displayed be based on a variable defined in the class.

First, you need to define the variable

content = 'This is a {View} template and is not used for much of anything but '
             'allowing extensions of it for other Views'

And then you can do something like this:

def get(self, _):
    return HttpResponse(self.content, content_type='text/plain')

Also, as mentioned above you can specify the allowable methods via the attribute http_method_names.

The following HTTP methods are allowed:

  • get
  • post
  • put
  • patch
  • delete
  • head
  • options
  • trace

By default all are allowed.

If we put all of the pieces together we can see that a really simple View CBV would look something like this:

class myView(View):
    content = 'This is a {View} template and is not used for much of anything but '
             'allowing extensions of it for other Views'
    http_method_names = ['get']

    def get(self, _):
        return HttpResponse(self.content, content_type='text/plain')

This View will return content to the page rendered as plain text. This CBV is also limited to only allowing get requests.

Here’s what it looks like in the browser:

View

Conclusion

View doesn’t do much, but it’s the case for everything else, so understanding it is going to be important.

Class Based Views

As I’ve written about previously I’m working on a Django app. It’s in a pretty good spot (you should totally check it out over at StadiaTracker.com) and I thought now would be a good time to learn a bit more about some of the ways that I’m rendering the pages.

I’m using Class Based Views (CBV) and I realized that I really didn’t grok how they worked. I wanted to change that.

I’ll be working on a series where I deep dive into the CBV and work them from several angles and try to get them to do all of the things that they are capable of.

The first place I’d suggest anyone start to get a good idea of CBV, and the idea of Mixins would be SpaPas’ GitHub Page where he does a really good job of covering many pieces of the CBV. It’s a great resource!

This is just the intro to this series and my hope is that I’ll publish one of these pieces each week for the next several months as I work my way through all of the various CBV that are available.

My first project after completing the 100 Days of Web in Python

As I mentioned in my last post, after completing the 100 Days of Web in Python I was moving forward with a Django app I wrote.

I pushed up my first version to Heroku on August 24. At that point it would allow users to add a game that they had seen, but when it disaplyed the games it would show a number (the game’s ID) instead of anything useful.

A few nights ago (Aug 28) I committed a version which allows the user to see which game they add, i.e. there are actual human readable details versus just a number!

The page can be found here. It feels really good to have it up in a place where people can actually see it. That being said I discovered a a couple of things on the publish that I’d like to fix.

I have a method that returns details about the game. One problem is that if any of the elements return None then the front page returns a Server 500 error ... this is not good.

It took a bit of googling to see what the issue was. The way I found the answer was to see an idea to turn Debug to True on my ‘prod’ server and see the output. That helped me identify the issue.

To ‘fix’ it in the short term I just deleted all of the data for the games seen in the database.

I’m glad that it happened because it taught me some stuff that I knew I needed to do, but maybe didn’t pay enough attention to ... like writing unit tests.

Based on that experience I wrote out a roadmap of sorts for the updates I want to get into the app:

  • Tests for all classes and methods
  • Ability to add minor league games
  • Create a Stadium Listing View
  • More robust search tool that allows a single team to be selected
  • Logged in user view for only their games
  • Create a List View of games logged per stadium
  • Create a List View of attendees (i.e. users) at games logged
  • Add more user features:
    • Ability to add a picture
    • Ability to add Twitter handle
    • Ability to add Instagram handle
    • Ability to add game notes
  • Create a Heroku Pipeline to ensure that pushes to PROD are done through a UAT site
  • Create a blog (as a pelican standalone sub domain)

It’s a lot of things but I’ve already done some things that I wanted to:

  • Added SSL
  • Set up to go to actual domain instead of Heroku subdomain

I’ll write up how I did the set up for the site so I can do it again. It’s not well documented when your registrar is Hover and you’ve got your site on Heroku. Man ... it was an tough.

Whoops! Or how I broke my website by installing Nginx with Apache

I’ve been working on a project to create a Django based website. Over the weekend (Saturday I think) I tried to get it up and running on my Linode server. However, after a couple of failed attempts I decided to use the free hosting coupon1 I had for DigitalOcean to see if that allowed me to reply more easily deploy … the short answer … meh.

What I didn’t realize over the weekend is that while I had been trying to deploy my Django site, I had installed Nginx on my Linode server that was also running apache2. This lead to them both trying to listen on port 80 but because Nginx was the last thing I had kicked off, it was winning.

While I was working on my Django site I should have realized that something was up when I tried to connect to the blog for the site (still a Wordpress site on my Linode server) and it returned a ‘Can not connect to the server message’. I didn’t pay much attention because I figured (incorrectly) that I had done something specific to that subdomain, and not that I had made all of the sites on my Linode server inaccessible.

Last night at about 9 I thought, “Well, it should’t take long for me to figure out the issue with the new blog. ”

By 10:15 I tried everything the internet had told me to try and I was still unable to get apache2 to reload.

I googled a bunch of stuff, but nothing was helping.

When I tried to get the status on apache2 I would get this:

 apache2.service - LSB: Apache2 web server
   Loaded: loaded (/etc/init.d/apache2; bad; vendor preset: enabled)
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: inactive (dead) since Tue 2018-05-01 05:01:03 PDT; 5s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 7718 ExecStop=/etc/init.d/apache2 stop (code=exited, status=0/SUCCESS)
  Process: 7703 ExecStart=/etc/init.d/apache2 start (code=exited, status=0/SUCCESS)

May 01 05:01:03 milo apache2[7703]: (98)Address already in use: AH00072: make_sock: could not bind to address [::]:80
May 01 05:01:03 milo apache2[7703]: (98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80
May 01 05:01:03 milo apache2[7703]: no listening sockets available, shutting down
May 01 05:01:03 milo apache2[7703]: AH00015: Unable to open logs
May 01 05:01:03 milo apache2[7703]: Action 'start' failed.
May 01 05:01:03 milo apache2[7703]: The Apache error log may have more information.
May 01 05:01:03 milo apache2[7703]:  *
May 01 05:01:03 milo apache2[7718]:  * Stopping Apache httpd web server apache2
May 01 05:01:03 milo apache2[7718]:  *
May 01 05:01:03 milo systemd[1]: Started LSB: Apache2 web server.

This morning I started to google each line of the status message and finally got to this:

no listening sockets available, shutting down

Googling for that lead me to trying this:

sudo netstat -ltnp | grep ':80'

Which output this:

tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      3324/nginx -g daemo
tcp6       0      0 :::80                   :::*                    LISTEN      3324/nginx -g daemo

And finally, I saw the issue. Over the weekend while I was futzing around I had apparently installed Nginx and let it listen on port 80 AND kept it running.

Once I killed the Nginx process with this:

sudo kill -9 3324

I was able to restart apache2 with no problems.

Thank goodness.

I find that when I mess something up like this it’s important to ask myself what I learned from the experience.

In that vein …

What did I learn from this experience?

  1. Can’t run apache2 and Nginx on the same server and have them listen on the same port. Seems obvious, but you know having to actually deal with it really seals the deal
  2. The output messages are super helpful … google each part of them and don’t give up
  3. A good night’s sleep can make all the difference
  4. Rolling your own web server is less expensive than having it be Turnkey (a la SquareSpace, or some other hosted solution) but you end up being your own Sys Admin and that’s actually pretty easy when things are going well, and a freaking nightmare when they’re not
  1. [Thanks to the Talk Python to Me Course for Entrepreneurs

Page 3 / 3