Django Form Credit Card Field with Pattern, Length and Luhn Validation

I’ve been doing a bit of ecommerce work lately and one of my needs was a credit card field for a Django form. Ideally it needed to support all major cards (PayPal offers a good reference list) and be easily extendable in the future.

All I could find via Google was this older Django project and various blog posts, none of which really fit my needs.

After a bit more digging I decided to port over portions of Stripe’s jQuery.payment which does almost exactly what I needed.

You can then use this field in your form:

Limiting Upload File Size with Django Forms

A common use case in any web application is to allow users to upload files such as images, videos, PDFs, etc. But left unrestricted, this could lead to all sorts of problems including users uploading files that are too large for the system or application to handle.

File size restrictions can be placed on the upload in multiple ways including with JavaScript, web server configuration changes and within your application code. I definitely recommend using all of the above, but for this post, we’ll look at how to handle this in Django by subclassing Django’s FileField and ImageField form fields and adding some extra logic to the clean method.

First, define a max upload size within your settings (in bytes).

Next, define the new fields (I usually stick these in app/forms/fields.py):

You can now make use of these new fields from within your form class. Note that content_types is required for RestrictedFileField while max_upload_size is optional for both fields (defaults to whatever you specified for MAX_UPLOAD_SIZE in your settings).

And remember that the content-type is still user supplied (i.e. it’s a header coming from whatever submitted the form), so be sure to verify that the uploaded file contains the content-type you’re expecting.

Django ModelForm and Conditionally Disabled (Readonly) Fields

I ran into a use case today that required a model form to have some fields disabled (readonly), but only when a certain condition was met (specifically when a property of the model was set to true) . Since the model form is used with the fields both enabled and disabled, simply duplicating the model form and removing the necessary fields wasn’t an option (plus that’s not exactly DRY now is it).

The solution ended up being quite simple. First, you’ll need to override the clean method for each field that needs to be disabled within your model form class:

The clean field method simply checks the is_disabled property and if it’s true, returns the existing field data. It only returns the new submitted data, pulled from cleaned_data, if is_disabled is false.

The second part of the solution is to set the field to readonly in your template. Note that you need to use readonly rather than disabled as the field won’t actually be submitted if set to disabled (more info on this here). This will cause Django to throw a missing field error before the clean field method ever runs.

You can add the necessary html to the form field via the widget’s attributes property:

Django Form Inheritance

Form inheritance (docs) in Django is extremely straightforward:

You can also subclass the parent’s Meta inner class if you want to change the Meta.fields or Meta.excludes lists:

But what if you want to add to the fields rather than replace them? This is actually straightforward as well:

Django Form Wizard and Getting Data from Previous Steps

I’ve spent the last few days implementing a Django SessionWizardView and ran into what I thought would be a simple problem – how to access the data of a previous form in order to initialize a later form (i.e. use data submitted on step 1 to initialize the step 3 form). Google didn’t turn up much so I turned to the documentation for a solution.

At first glance it seemed simple enough – use get_cleaned_data_for_step or get_all_cleaned_data. The problem with these, I quickly discovered, is that they call get_form which in turn calls get_form_initial (which is where I’m trying to use the submitted form data in the first place). So that was a dead end.

Next up was using get_form_step_data. This method is called before get_form_initial and has access to both the form’s data and cleaned_data properties. The issue with this technique, however, is that it would require the data to be stored in some way for use in get_form_initial (either via a session variable or an instance variable). While this works, it still seemed like there had to be a better way. After all, the wizard is storing this data somewhere already.

So after a bit of digging in the source, the solution was obvious – self.storage.get_step_data(step).

This method allows you to pull the data from any step and will return either None or a MultiValueDict.

One final thing to note is that because get_form_initial is called for every previous step (with that previous step as the step argument), it’s probably likely you’ll need to know what step you’re actually on. You can do this by using self.storage.current_step.

If anyone knows of a better way to do this, I’d love to know.

Django Class-based Views with Multiple Forms

By default some of Django’s class-based views support just a single form per view. This, of course, does not always play nicely with what you’re trying to accomplish. I’ve yet to come across a concrete example of how to accomplish this so after a bit of experimentation and some time spent diving into Django’s code, I’ve created a decent workaround. Please let me know in the comments if there’s a better way to do this.

For this example I’ll use a generic UpdateView and two ModelForms. The same technique should work with most of the class-based views as well as regular forms.

I’ll also mention that this handles the validation and submission of either form, but not both forms at the same time. The user is only submitting a single form, so you only need to handle that one.

There’s nothing special about the models or the templates, so let’s look at the view:

The first thing the view does is set the template, success_url and the two forms. Next we define the get_context_data method which adds either form to the context if it is missing.

UPDATE: There was an issue with the get_context_data method in which the form would not be populated with values from the model if a form was submitted with invalid data (and self.form_invalid was called). The current fix is to manually pass the data to the form using the initial argument.

The get_object method overrides the default behavior and gets a model instance based on a session variable. This isn’t necessary if you’re using the standard method of providing a pk or slug in the URL.

The next change is very important – override the default form_invalid method. The default method returns a single form in the context:

Since we have two forms, with different names, we need to switch to using **kwargs instead.

Finally, we override the post method to determine which form was submitted based on the name of the submit button (you could alternatively use a hidden field), validate the form and call form_valid or form_invalid. These methods will save the form and redirect to the success_url, or return to the view and show any errors, respectively.

And for the sake of clarity, the forms in your templates simply need a named submit button: