/var/

Various programming stuff

Hello! If you are using an ad blocker but find something useful here and want to support me please consider disabling your ad blocker for this site.

Thank you,
Serafeim

Change the primary color of bootstrap material design

Introduction

Bootstrap Material Design is a great theme that sits on top of Bootstrap and transforms it to Material Design! The great thing about Bootstrap Material Design is that you just need to include its css and js files after your Bootstrap files and …

boom! Your page is Material Design compatible!

A nice feature of Bootstrap Material Design is that you can change its default color to a new one (I don’t really like the current - greenish one). This is easy for people with less skills however I found it rather challenging when I tried it. That’s why I will present a step by step tutorial on changing the default primary color of the Bootstrap Material Design theme:

Step 1: Get the code

Use git to make a local clone of the project with git clone https://github.com/FezVrasta/bootstrap-material-design.git. This will create a directory named bootstrap-material-design. Or you can download the latest version of the code using (https://github.com/FezVrasta/bootstrap-material-design/archive/master.zip) and unzip it to the bootstrap-material-design directory.

Step 2: Install node.js and npm

You need to have node.js and npm installed in your system - this is something very easy so I won’t go into any details about this. After you have installed both node.js and npm you need to put them in your path so that you’ll be able to run npm -v without errors and receive something like 1.4.14.

Step 3: Install less

less is a CSS preprocessor in which Bootstrap Material Design has been written. To install it, just enter the command npm install -g less. After that you should have a command named lessc which, when run would output something like: lessc 2.1.1 (Less Compiler) [JavaScript].

Step 4: Create the customizations files

Go to the directory where you cloned (or unzipped) the Bootstrap Material Design code and create a file named custom.less (so, that file should be in the same folder as with bower.json, Gruntfile.js etc) with the following contents:

@import "less/material.less";

// Override @primary color with one took from _colors.less
@primary: @indigo;

(I wanted to use the indigo color as my primary one - you may of course use whichever color from the ones defined in less/_variables.less you like)

This file may contain other default values for variables - if I find anything useful I will add it to this post (also please reply with any recommendations).

Update 13/10/2015 After a request from commenter Jofferson Ramirez Tiquez, here’s a custom.less that overrides more colors from _variables.css (beyond the primary color, it changes the success color to teal and info and warning to the corresponding hex color values):

@import "less/material.less";

@primary: @indigo;
@success: @teal;
@info: #CFD8DC;
@warning:#455A64;

Update 08/02/2017 Commenter Enrique SIlva informed me that the names of these color variables have been changed to @brand-primary, @brand-success etc (as can be seen on less/_variables.less) so you must change them accordingly when overriding them!

Step 5: Create your custom material css file

Finally, run the following command: lessc custom.less  > material-custom.css. This will create a file named material-custom.css that contains your custom version of Bootstrap Material Design! If you want your material-custom.css to be compressed, add the -x option like this: lessc -x custom.less  > material-custom.css.

You may now include material-custom.css instead of material.css (or the minified version of it) to your projects and you’ll have your own primary color!

Update 09/06/2016: After some comments I returned to this blog post and tried to re-create the custom css file. Unfortunately, for the latest versions of the bootstrap-material-design, the above step by step instructions need to be changed in two places:

  • Step 1: Instead of only the bootstrap-material-design code, you also need to retrieve the code of the original bootstrap. To do that you can do a git clone https://github.com/twbs/bootstrap.git so that you’ll have the bootstrap directory next to the bootstrap-material-design directory. Be careful on that, you should have two directories side by side, one containing the bootstrap code and another containing the bootstrap-material-design code. This is required because bootstrap-material-design references some bootstrap variables (from less/_import-bs-less.less).
  • Step 4: For the customizations file (custom.less), you should change the line @import "less/material.less"; to @import "less/bootstrap-material-design.less"; because the name of that file has been changed.

After these two changes you should once again be able to create your custom css file!

Update 03/07/2017 Commenter Nuraan Bader informed of an NameError: variable @line-height-base is undefined error when creating the custom css file. This is because bootstrap has changed the default branch from master to v4-dev so when you clone you’ll get the v4-dev branch instead of the master one! To fix this, after you clone (check out my 09/06/2016 update) do the following:

cd bootstrap
git checkout master

This should change the current branch to master and you should see the less folder inside the bootstrap folder. After that everything should work as expected.

Retrieving Gmail blocked attachments

Before services like Dropbox were widely available, some people (including me) were using their Gmail account as a primitive backup solution: Compress your directory and send it to your gmail. There. Backup complete.

However, nothing is so easy…

Recently, I wanted to retrieve one of these backups, a .rar containing the complete source code (since it was written in TeX) of my PhD thesis. The problem was that Gmail blocked the access to these attachments saying

Anti-virus warning - 1 attachment contains a virus or blocked file. Downloading this attachment is disabled.

probably because I had a number of .bat files inside that .rar archive to automate my work :(

Now what ?

After searching the internet and not founding any solutions, I tried the options that gmail gives for each email. One particular one cought my interest: Show original

Here it is!

Clicking this option opened a text file with the original, MIME encoded message. The interesting thing of course was

------=_NextPart_000_004F_01CA0AED.E63C2A30
Content-Type: application/octet-stream;
      name="phdstuff.rar"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
      filename="phdstuff.rar"

UmFyIRoHAM+QcwAADQAAAAAAAAB0f3TAgCwANAMAAFQEAAACRbXCx8lr9TodMwwAIAAAAG5ld2Zp
bmFsLnR4dA3dEQzM082BF7sB+D3q6QPUNEfwG7vHQgNkiQDTkGvfhOE4mNltIJJlBFMOCQPzPeKD
...

So the whole attachment was contained in that text file, encoded in base64! Now I just needed to extract it from the email and convert it back to binary.

Important: Before going the python way, please check the 2 June 2015 update at the end of the article for an easier solution!

This was very easy to do using Python - some people had already asked the same thing on SO. So here’s a simple program that gets an email in text/mime format as input and dumps all attachments:

import email
import sys

if __name__=='__main__':
    if len(sys.argv)<2:
        print "Please enter a file to extract attachments from"
        sys.exit(1)

    msg = email.message_from_file(open(sys.argv[1]))
    for pl in msg.get_payload():
        if pl.get_filename(): # if it is an attachment
            open(pl.get_filename(), 'wb').write(pl.get_payload(decode=True))

Save this to a file named get_attachments.py and, after saving the original message to a file named 0.txt run python get_attachments.py 0.txt and you’ll see the attachments of your email in the same folder!

Disclaimer: I have to warn you that since Gmail claims that an attachment is not safe it may be actually not safe. So you must be 100% sure that you know what you are doing before retrievening your email attachments like this.

Update: Stefan created an improved version of the attachment extractor which is also compatible with Python 3.4!

Update, 12 January 2015: Ivana (at the comments section) proposed a different solution that may work for some files: Use a mobile Gmail client (I tested it with Android) and “Save to Drive” your attachment. You’ll then be able to download it from the Google Drive! I am not sure if this works for all attachments, however it worked for the source of my PhD thesis! I’m writing it may not work for all attachments because when you download something from Google Drive it does a virus check so it may not allow you to download the attachment and then you’ll still need to do it manually using the method below (however in that case you must be even more cautious for the case the attachment actualyl contains a malicious file).

Update, 2 June 2015: Commenter Sumit Chauhan (and Yuri Marx) proposed to change the extension of the downloaded MIME text file (original message) to eml and open it with Outlook. I don’t have Outlook in my system, however I tried opening it with Thunderbird and it worked!!! So please try this solution before trying the pythonic way (especially if you’re not familiar with python).

Update, 6 March 2016: Commenter Alex (Alexandre Barfuhok) proposed another clever trick (involving making visible the initially invisible “Download to drive” button) for retrieving gmail blocked attachments! Read more about it at his article!

Django non-HTML responses

Introduction

I have already written about the many advantages (DRY!) of using CBVs in a previous article. In this article I will present the correct (pythonic) way to allow a normal CBV to return non-HTML responses, like PDF, CSV, XSL etc.

How are CBVs rendered

Before proceeding, we need to understand how CBVs are rendered. By walking through the class hierarchy in the CBV inspector, we can see that all the normal Django CBVs (DetailView, CreateView, TemplateView etc) are using a mixin named TemplateResponseMixin which defines a method named render_to_response. This is the method that is used for rendering the output of the Views. Let’s take a look at how it is defined (I’ll remove the comments):

class TemplateResponseMixin(object):
  template_name = None
  response_class = TemplateResponse
  content_type = None

  def render_to_response(self, context, **response_kwargs):
      response_kwargs.setdefault('content_type', self.content_type)
      return self.response_class(
          request=self.request,
          template=self.get_template_names(),
          context=context,
          **response_kwargs
      )

  def get_template_names(self):
      if self.template_name is None:
          raise ImproperlyConfigured(
              "TemplateResponseMixin requires either a definition of "
              "'template_name' or an implementation of 'get_template_names()'")
      else:
          return [self.template_name]

This method will try to find out which html template should be used to render the View (using the get_template_names method and template_name attribute of the mixin) and then render this view by instantiating an instance of the TemplateResponse class (as defined in the response_class attribute) and passing the request, template, context and other response_args to it.

The TemplateResponse class which is instantiated in the render_to_response method inherits from a normal HttpResponse and is used to render the template passed to it as a parameter.

Rendering to non-HTML

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 response_class attribute (and also change the content_type attribute). On the other hand, if your non-HTML respond does not need a template to be rendered then you have to override render_to_response 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 render_to_response.

Since almost always you won’t need a template to create a non-HTML view and because I believe that the solution is DRY-enough by implementing the rendering code to the render_to_response method (without subclassing HttpResponse) I will implement a mixin that does exactly that.

Subclassing HttpResponse will not make our design more DRY because for every subclass of HttpResponse the render_to_response method would also need to be modified (by subclassing TemplateResponseMixin) to instantiate the subclass of ``HttpResponse with the correct parameters. For instance, the existing TemplateResponseMixin cannot be used if the subclass of HttpResponse does not take a template as a parameter (solutions like passing None to the template parameter are signals of bad design).

In any case, changing just the render_to_response method using a Mixin is in my opinion the best solution to the above problem. A Mixin is a simple class that can be used to extend other classes either by overriding functionality of the base class or by adding extra features. Django CBVs use various mixins to extend the base Views and add functionality.

A non-HTML mixin

So, a basic skeleton for our mixin would be something like this:

class NonHtmlResponseMixin(object):
    def render_to_response(self, context, **response_kwargs):
        response = HttpResponse(content_type='text/plain')
        response.write( "Hello, world" )
        return response

The previous mixin overrides the render_to_response method to just return the text “Hello, world”. For instance we could define the following class:

class DummyTextResponseView(NonHtmlResponseMixin, TemplateView,):
  pass

which can be added as a route to urls.py (using the as_view method) and will always return the “Hello, world” text.

Here’s something more complicated: A Mixin that can be used along with a DetailView and will output the properties of the object as text:

class TextPropertiesResponseMixin(object):
  def render_to_response(self, context, **response_kwargs):
      response = HttpResponse(content_type='text/plain; charset=utf-8')
      o = self.get_object()
      o._meta.fields
      for f in o._meta.fields:
          response.write (u'{0}: {1}\n'.format(f.name,  unicode(o.__dict__.get(f.name)) ) )
      return response

and can be used like this

class TextPropertiesDetailView(TextPropertiesResponseMixin, FooDetailView,):
  pass

The above mixin will use the get_object() method of the DetailView to get the object and then output it as text. We can create similar mixins that will integrate with other types of CBVs, for instance to export a ListView as an CSV or generate an png from a DetailView of an Image file.

A more complex example

The previous examples all built upon an existing view (either a TemplateView, a DetailView or a ListView). However, an existing view that will fit our requirements won’t always be available. For instance, sometimes I want to export data from my database using a raw SQL query. Also I’d like to be able to easily export this data as csv or excel.

First of all, we need to define a view that will inherit from View and export the data as a CSV:

import unicodecsv
from django.db import connection
from django.views.generic import View

class CsvRawSqlExportView(View, ):
  sql = 'select 1+1'
  headers = ['res']
  params = []

  def get(self, request):
      def generate_data(cursor):
          for row in cursor.fetchall():
              yield row

      cursor = connection.cursor()
      cursor.execute(self.sql, self.params)
      generator = generate_data(cursor)
      return self.render_to_response(generator)

  def render_to_response(self, generator, **response_kwargs):
      response = HttpResponse(content_type='text/plain; charset=utf-8')
      response['Content-Disposition'] = 'attachment; filename=export.csv'
      w = unicodecsv.writer(response, encoding='utf-8')
      w.writerow(self.headers)
      for row in generator:
          w.writerow(row)

      return response

The above View has three attributes: * sql, which is a string with the raw sql that will be executed * headers, which is an array with the names of each header of the resulting data * params, which is an array with parameters that may need to be passed to the query

The get method executes the query and passes the result to render_to_response using a generator. The render_to_response method instantiates an HttpResponse object with the correct attributes and writes the CSV to the response object using unicodecsv.

We can now quickly create a route that will export data from the users table:

url(
    r'^raw_export_users/$',
    views.CsvRawSqlExportView.as_view(
        sql='select id, username from auth_user',
        headers=['id', 'username']
    ) ,
    name='raw_export_users'
),

If instead of CSV we wanted to export to XLS (using xlwt), we’d just need to create a Mixin:

class XlsRawSqlResponseMixin(object):
  def render_to_response(self, generator, **response_kwargs):
      response = HttpResponse(content_type='application/ms-excel')
      response['Content-Disposition'] = 'attachment; filename=export.xls'
      wb = xlwt.Workbook(encoding='utf-8')
      ws = wb.add_sheet("data")

      for j,c in enumerate(self.headers):
              ws.write(0,j,c)

      for i,row in enumerate(generator):
          for j,c in enumerate(row):
              ws.write(i+1,j,c)

      wb.save(response)
      return response

and create a View that inherits from CsvRawSqlExportView and uses the above mixin:

class XlsRawSqlExportView( XlsRawSqlResponseMixin, CsvRawSqlExportView ):
  pass

and route to that view to get the XLS:

url(
    r'^raw_export_users/$',
    views.XlsRawSqlExportView.as_view(
        sql='select id, username from auth_user',
        headers=['id', 'username']),
    name='raw_export_users'
),

Conclusion

Using the above techniques we can define CBVs that will output their content in various content types beyond HTML. This will help us write write clean and DRY code.

Implementing a simple, Heroku-hosted REST service using Flask and mongoDB

Introduction

In the following, I will describe how I used Flask, a very nice web microframework for python along with mongoDB, the most popular No-SQL database to implement a simple REST service that was hosted on Heroku. This REST service would get readings from a number of sensors from an Android device.

I chose Flask instead of Django mainly because the REST service that I needed to implement would be very simple and most of the Django bells and whistles (ORM, auth, admin, etc) wouldn’t be needed anyway. Also, Flask is much quicker to set-up than Django since almost everything (views, urls, etc) can be put inside one python module.

Concerning the choice of a NoSQL persistance solution (mongoDB), I wanted to have a table (or collection as it is called in the mongoDB world) of readings from the sensor. Each reading would just have a timestamp and various other arbitrary data depending on the type of the reading, so saving it as a JSON document in a NoSQL database is a good solution.

Finally, all the above will be deployed to Heroku which offers some great services for deploying python code in the cloud.

Requirements

I propose creating a file named requirements.txt that will host the required packages for your project, so you will be able to setup your projects after creating a virtual environment with virtualenv just by running pip install -r requirements.txt. Also, the requirements.txt is required for deploying python to Heroku.

So, for my case, the contents of requirements.txt are the following:

Flask==0.10.1
Flask-PyMongo==0.3.0
Flask-RESTful==0.2.12
Jinja2==2.7.3
MarkupSafe==0.23
Werkzeug==0.9.6
aniso8601==0.82
gunicorn==19.0.0
itsdangerous==0.24
pymongo==2.7.1
pytz==2014.4
six==1.7.2
  • Flask-PyMongo is a simple wrapper for Flask around pymongo which is the python mongoDB driver.
  • Flask-RESTful is a simple library for creating REST APIs - it needs aniso8601 and pytz.
  • Jinja2 is the template library for Flask (we won’t use it but it is required by Flask installation) - it needs MarkupSafe.
  • Werkzeug is a WSGI utility library - required by Flask
  • gunicorn is a WSGI HTTP server - needed for deployment to Heroku
  • itsdangerous is used to sign data for usage in untrusted environments
  • six is the python 2/3 compatibility layer

Implementing the REST service

Instead of using just one single file for our Flask web application, we will create a python module to contain it and a file named runserver.py that will start a local development server to test it:

So, in the same folder as the requirements.txt create a folder named flask_rest_service and in there put two files: __init__.py and resources.py.

The __init__.py initializes our Flask application, our mongoDB connection and our Flask-RESTful api:

import os
from flask import Flask
from flask.ext import restful
from flask.ext.pymongo import PyMongo
from flask import make_response
from bson.json_util import dumps

MONGO_URL = os.environ.get('MONGO_URL')
if not MONGO_URL:
    MONGO_URL = "mongodb://localhost:27017/rest";

app = Flask(__name__)

app.config['MONGO_URI'] = MONGO_URL
mongo = PyMongo(app)

def output_json(obj, code, headers=None):
    resp = make_response(dumps(obj), code)
    resp.headers.extend(headers or {})
    return resp

DEFAULT_REPRESENTATIONS = {'application/json': output_json}
api = restful.Api(app)
api.representations = DEFAULT_REPRESENTATIONS

import flask_rest_service.resources

So what happens here? After the imports, we check if we have a MONGO_URL environment variable. This is how we set options in Heroku. If such option does not exist in the environment then we are in our development environment so we set it to the localhost (we must have a running mongoDB installation in our dev environment).

In the next lines, we initialize our Flask application and our mongoDB connection (pymongo uses a MONGO_URI configuration option to know the database URI).

The output_json is used to dump the BSON encoded mongoDB objects to JSON and was borrowed from alienretro’s blog — we initialize our restful REST API with this function.

Finally, we import the resources.py module which actually defines our REST resources.

import json
from flask import request, abort
from flask.ext import restful
from flask.ext.restful import reqparse
from flask_rest_service import app, api, mongo
from bson.objectid import ObjectId

class ReadingList(restful.Resource):
    def __init__(self, *args, **kwargs):
        self.parser = reqparse.RequestParser()
        self.parser.add_argument('reading', type=str)
        super(ReadingList, self).__init__()

    def get(self):
        return  [x for x in mongo.db.readings.find()]

    def post(self):
        args = self.parser.parse_args()
        if not args['reading']:
            abort(400)

        jo = json.loads(args['reading'])
        reading_id =  mongo.db.readings.insert(jo)
        return mongo.db.readings.find_one({"_id": reading_id})


class Reading(restful.Resource):
    def get(self, reading_id):
        return mongo.db.readings.find_one_or_404({"_id": reading_id})

    def delete(self, reading_id):
        mongo.db.readings.find_one_or_404({"_id": reading_id})
        mongo.db.readings.remove({"_id": reading_id})
        return '', 204


class Root(restful.Resource):
    def get(self):
        return {
            'status': 'OK',
            'mongo': str(mongo.db),
        }

api.add_resource(Root, '/')
api.add_resource(ReadingList, '/readings/')
api.add_resource(Reading, '/readings/<ObjectId:reading_id>')

Here we define three Resource classes and add them to our previously defined api: Root, Reading and ReadingList.

Root just returns a dictionary with an OK status and some info on our mongodb connection.

Reading has gets an ObjectId (which is the mongodb primary key) as a parameter and depending on the HTTP operation, it returns the reading with that id when receiving an HTTP GET and deletes the reading with that id when receiving an HTTP DELETE.

ReadingList will return all readings when receiving an HTTP GET and will create a new reading when receiving an HTTP POST The post function uses the parser defined in __init__ which requires a reading parameter with the actual reading to be inserted.

Testing it locally

In order to run the development server, you will need to install and start mongodb locally which is beyond the scope of this post. After that create a file named runserver.py in the same folder as with the requirements.txt and the flask_rest_service folder. The contents of this file should be:

from flask_rest_service import app
app.run(debug=True)

When you run this file with python runserver.py you should be able top visit your rest service at http://localhost:5000 and get an “OK” status.

Deploying to Heroku

To deploy to Heroku, you must create a Procfile that contains the workers of your application. In our case, the Procfile should contain the following:

web: gunicorn flask_rest_service:app

Also, you should add a .gitignore file with the following:

*.pyc

Finally, to deploy your application to Heroku you can follow the instructions here: https://devcenter.heroku.com/articles/getting-started-with-python:

  • Initialize a git repository and commit everything:
git init
git add .
git commit -m
  • Create a new Heroku application (after logging in to heroku with heroku login) and set the MONGO_URL environment variable (of course you have to obtain ths MONGO_URL variable for your heroku envirotnment by adding a mongoDB database):
heroku create
heroku config:set MONGO_URL=mongodb://user:pass@mongoprovider.com:27409/rest
  • And finally push your master branch to the heroku remote repository:
git push heroku master

If everything went ok you should be able to start a worker for your application, check that the worker is running, and finally visit it:

heroku ps:scale web=1
heroku ps
heroku open

If everything was ok you should see an status-OK JSON !

Django generic FormViews for objects

Introduction

We recently needed to create a number of views for changing the status of an Application model instance for our organization. An Application model instance can be filled and then cancelled, submitted, acceptted etc - for each of these status changes a form should be presented to the user. When the user submits the form the status of the Application will be changed.

To implement the above requirement we created a generic FormView that acts on the specific model instance. This used two basic CBV components: The FormView for the form manipulation and the SingleObjectMixing for the object handling.

Django Class Based Views (CBVs) can be used to create reusable Views using normal class inheritance. Most people use the well-known CreateView, UpdateView, DetailView and ListView, however, as we will see below, the FormView will help us write DRY code.

I have to notice here that an invaluable tool to help you understanding CBVs is the CBV inspector which has a nice web interface for browsing the CBV hierarchies, attributes and methods.

A quick introduction to the FormView

A simple FormView can be defined like this (CBV FormView):

class MyFormView(FormView):
  form_class = forms.MyFormView
  template_name = 'my_template.html'

The above can be used in urls.py like this:

urlpatterns = patterns('',
  url(r'^my_formview/$', views.MyFormView.as_view() , name='my_formview' ),

This will present a form to the user when he visits the my_formview url — however this form won’t do anything. To allow the form to actually do something when it’s been submitted we need to override the form_valid method.

def form_valid(self, form):
    value = form.cleaned_data['value']
    messages.info(self.request, "MyForm submitted with value {0}!".format(value) )
    return HttpResponseRedirect( reverse('my_formview') )

As you can see the submitted form is passed in the method and can be used to receive its cleaned_data. The FormView has various other options for instance a form_invalid method, an initial attribute to set the initial values for the form etc.

A quick introduction to the SingleObjectMixin

A SingleObjectMixin adds a number of attributes & methods to a view that can be used for object manipulation. The most important ones is the model and queryset attributes and the get_queryset and get_object(). To use the SingleObjectMixin in your CBV just add it to the list of the classes to inherit from and define either the model or the queryset attribute. After that you may pass a pk parameter to your view and you will get an object context variable in the template with the selected object!

Being generic and DRY

We can more or less now understand how we should use FormView and SingleObjectMixin to generate our generic FormView for acting on objects: Our FormView should get the object using the SingleObjectMixin and change it when the form is submitted using the values from the form. A first implementation would be the following:

class GenericObjectFormView1(FormView, SingleObjectMixin):

    def form_valid(self, form):
        obj = self.get_object()
        obj.change_status(form)
        return HttpResponseRedirect( obj.get_absolute_url() )

So our GenericObjectFormView1 class inherits from FormView and SingleObjectMixin. The only thing that we have to assure is that the Model we want to act on needs to implement a change_status method which gets the form and changes the status of that object based on its value. For instance, two implementations can be the following:

class CancelObjectFormView(GenericObjectFormView1):
    template_name = 'cancel.html'
    form_class = forms.CancelForm
    model = models.Application

class SubmitObjectFormView(GenericObjectFormView1):
    template_name = 'submit.html'
    form_class = forms.SubmitForm
    model = models.Application

Being more generic and DRY

The previous implementation has two problems:

  • What happens if the status of the object should not be changed even if the form is valid?
  • We shouldn’t need to create a new template for every new GenericObjectFormView since all these templates will just output the object information, ask a question for the status change and output the form.

Let’s write a new version of our GenericObjectFormView that actually resolves these:

class GenericObjectFormView2(FormView, SingleObjectMixin):
    template_name = 'generic_formview.html'
    ok_message = ''
    not_ok_message = ''
    title = ''
    question =''

    def form_valid(self, form):
        obj = self.get_object()
        r = obj.change_status(form)
        if r:
            messages.info(self.request, self.yes_message)
        else:
            messages.info(self.request, self.not_ok_message)
        return HttpResponseRedirect( obj.get_absolute_url() )

    def get_context_data(self, **kwargs):
        context = super(GenericYesNoFormView2, self).get_context_data(**kwargs)
        context['title'] = self.title
        context['question'] = self.question
        return context

The above adds an ok and not ok message which will be outputed if the status can or cannot be changed. To accomplish this, the change_status method should now return a boolean value to mark if the action was ok or not. Also, a generic template will now be used. This template has two placeholders: One for the title of the page (title attribute) and one for the question asked to the user (question attribute). Now we can use it like this:

class CancelObjectFormView(GenericObjectFormView2):
    form_class = forms.CancelForm
    model = models.Application
    ok_message = 'Cancel success!'
    not_ok_message = 'Not able to cancel!'
    title = 'Cancel an object'
    question = 'Do you want to cancel this object?'

class SubmitObjectFormView(GenericObjectFormView2):
    form_class = forms.SubmitForm
    model = models.Application
    ok_message = 'Submit  ok'
    not_ok_message = 'Cannot submit!'
    title = 'Submit an object'
    question ='Do you want to submit this object?'

Other options

We’ve just got a glimpse of how we can use CBVs to increase the DRYness of our Django applications. There are various extra things that we can add to our GenericObjectFormView2 as attributes which will be defined by inheriting classes. Some ideas is to check if the current user actually has access to modify the object (hint: override the get_object method of SingleObjectMixin) or render the form diffirently depending on the current user (hint: override the get_form_kwargs method of FormView).