Create or Update with a Django ModelForm

I recently had a use case where I needed to have a single form that could act as either a create or update form. The tricky part was that I wouldn’t know which one was necessary until the data was submitted by the user.

To solve this, I started with Django’s generic UpdateView and overwrote the get_object method so that it would either get the existing record or create a new one (using get_or_create) based on the data being submitted. In my case (and the example below), I had two values that determined whether a new record was necessary or if an existing one should be used.

The underlying ModelForm didn’t require any modification (you could even just use Django’s automatically generated ModelForm by specifying the model in your view).

Here’s the code:

from django.views.generic import UpdateView
from forms import MyModelForm
from models import MyModel
class MyUpdateView(UpdateView):
# specify a custom ModelForm
form_class = MyModelForm
# or simply specify the Model
# model = MyModel
def get_object(self, queryset=None):
# get the existing object or created a new one
obj, created = MyModel.objects.get_or_create(col_1=self.kwargs['value_1'], col_2=self.kwargs['value_2'])
return obj

view raw
view.py
hosted with ❤ by GitHub

Sort Django Query (Order By) Using Values Within IN()

In MySQL, you can use the FIELD() function to easily sort a result set by a list of ordered ids:

SELECT id, name
FROM table
WHERE name IN (9, 8, 1, 2, 7, 3)
ORDER BY FIELD(id, 9, 8, 1, 2, 7, 3)

view raw
query.sql
hosted with ❤ by GitHub

To accomplish this in Django, you can make use of the extra() QuerySet method to create an additional field in the SELECT statement which can then be using for sorting in the FIELD method.

ids = [9, 8, 1, 2, 7, 3]
results = Model.objects.filter(id__in=ids).extra(
select={'manual': 'FIELD(id,%s)' % ','.join(map(str, ids))},
order_by=['manual']
)

view raw
query.py
hosted with ❤ by GitHub

Combine 2 Django Querysets from Different Models

If you’ve ever tried to concatenating two or more querysets from different models (i.e. combined = queryset1 | queryset2), you’ve hit this lovely error:

Cannot combine queries on two different base models.

The solution to this is to use itertools.

from itertools import chain
result_list = list(chain(queryset1, queryset2))

view raw
query.py
hosted with ❤ by GitHub

This allows you to not only combine the querysets into a single iterable, but it also allows you to sort the entire set by a shared field such as the date created:

from itertools import chain
from operator import attrgetter
# ascending oreder
result_list = sorted(
chain(queryset1, queryset2),
key=attrgetter('date_created'))
# descending order
result_list = sorted(
chain(queryset1, queryset2),
key=attrgetter('date_created'),
reverse=True)

view raw
query.py
hosted with ❤ by GitHub

VendorDB – The First Six Months

We created VendorDB with the hope of simply making it easier to find and keep track of the great companies we partner with to do our jobs.

As long-time agency veterans, we know how tough it can be to find masters of the latest tech, or determine who to call for help when plans change, or how to manage a last minute triple-bid request. We were tired of the hand-me-down Excel file full of incomplete information and Google searches that usually came up empty.

Let’s not even get started on the various ‘comprehensive’ industry directories out there. Not only are they clunky, confusing and often fail to answer basic queries, most like to charge for the privilege of being listed. That’s just silly.

We honestly had no idea what to expect when we launched back in May but we knew that if we dealt with this problem on a regular basis, there might be a few other folks out there who did as well. As Fred Wilson says, the unselfish thing to do is to script for others, you never know who will face the same problem as you.

It looks like our hunch was right.

In the 6 months since launch we’ve seen users from over 100 countries (exactly 103 as of today) and an astounding number of vendors, agencies and brands.

Better yet, a surprising number of you have been kind enough to take time out of your day to sign in and leave a review for one or more of the companies that make VendorDB great. We sincerely thank you for that.

If you’re curious, here are the current top 10 countries:

1) United States 6) Sweden
2) Canada 7) Netherlands
3) United Kingdom 8) Brazil
4) India 9) France
5) Australia 10) Germany

We’ve got some fantastic things planned for 2015 but before we close out the year, we’ve got one more feature just about ready to go…

Want to know if your profile page is worth the $0.00 you’re spending on it?

Yeah, we knew you did.

In the coming weeks we’ll be rolling out profile metrics to all page managers. Just like Facebook, LinkedIn and the other big guys, we believe that you should see exactly how you benefit from maintaining a profile on our platform.

And while we can’t promise tons of traffic for everyone (for now), we can promise transparency and actual usage data which is whole lot more than you get from those other sites.

More specifics will be released in the near future but for now we can tell you that you’ll be able to see an in-depth snapshot of your profile’s activity over the previous 90 days. So if you haven’t already claimed your profile, there’s really no excuse not to (it is FREE after all).

The first six months have been an amazing ride and we look forward to continuing to help each and every one of you find amazing partners to work with.

– Chris Kief & Dan Murphy

(Reblogged from http://blog.vendordb.com/the-first-six-months/)

Apple Pay Website Integration – A Guess

Benedict Evans wrote a great post on Apple Pay and asked the question – what’s next?

Well here’s a guess (pulled from these tweets)…

1/12
I haven’t seen Apple Pay website integration discussed much yet

2/12
So here’s a guess as to how it will work…

3/12
At the beginning of a site’s checkout flow (i.e. before filling out any forms), you’ll click the  Pay button

4/12
The site will ping Apple via the API which will in turn ping your iOS device (similar to 2-factor auth)

5/12
You’ll select the card to pay with and use your thumb to approve

6/12
The site will then receive a payment token to charge the order to

7/12
Ideally the site could then pull your billing / shipping info from Apple to skip those forms as well

8/12
The only step remaining will be to hit the checkout button (perhaps you tap your thumb a second time to confirm)

9/12
Simple, clean and no more credit cards online

10/12
Bonus – an obvious extension is to use the same system for 2-factor auth

11/12
Rather than enter the code shown in Google Authenticator or Authy, just press your finger

12/12
Which begs the question, could you even remove the password requirement?

Using Typekit CDN Web Fonts Locally While Offline (No Internet Connection)

I’ve got an important demo coming up in a few weeks (more on that at a later date) and like any good presenter, I’m going to assume that things won’t work. WiFi will be slow or non-existant (maybe I won’t upgrade to Yosemite just yet), my local web server will mysteriously stop responding and I’ll drop my laptop on the way there.

So at a minimum I’ll need two computers prepped with a site that will run 100% correctly without an internet connection. A backup screencap of the demo is probably a good idea as well.

For the majority of the application, running offline won’t be an issue – point the config files at a local database, turn off the CDN, etc. But there’s one resource that is strictly web-based – Adobe’s Typekit. As of this writing they only provide CDN access to the fonts so you can’t simply download the font files and use them.

Presenting without the fonts is not an option so either I purchase the actual font files for a few hundred bucks or I figure out how to hack Typekit.

Obviously I’m going with option two so here we go…

If you’ve used Typekit before, you’ll be familiar with the following snippet they provide to add fonts to your site.

<script type="text/javascript" src="//use.typekit.net/lfz0vxv.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>

view raw
page.html
hosted with ❤ by GitHub

This JavaScript file is responsible for loading the CSS that contains the font data from Typekit’s CDN (the URL is set in line 9 – “f”:”//use.typekit.net…”). So the first thing to do is download this file and save it locally.

/*
Original
https://use.typekit.net/lfz0vxv.js
*/
/*
* For font license information, see the CSS file loaded by this JavaScript.
*/
if(!window.Typekit)window.Typekit={};window.Typekit.config={"c":[".tk-freight-sans-pro","\"freight-sans-pro\",\"Helvetica\",\"Arial\",sans-serif"],"f":"//use.typekit.net/c/7302fd/1w;freight-sans-pro,1,TJ9:N:i3,TJF:N:i5,TJ8:N:n3,TJB:N:n4,TJD:N:n5,TJG:N:n6/{format}{/extras*}?3bb2a6e53c9684ffdc9a9bf11e5b2a6273d805f491df729128ca517d0b865e0e191e7b5aee445efc6d4ab4dc94e67aefe35eac8915e2de0959b2bb14cb74eb97243001a4e12199258e040dfe98f737ffac5827d670b2821c337b4c001b82bb67b53127b8ef655ac395a8807eadc56b96dfce3ebaeaf23eda54a42b78fd6598bf206c475067f1648d3f4fcce42c1c8687de1fd8c8d7fdc3934dc65b290046cea982d0a7ac4abb5c8f802f88867e69","fn":["freight-sans-pro",["i3","i5","n3","n4","n5","n6"]],"k":"//use.typekit.net/{id}.js","p":"//p.typekit.net/p.gif?s=1&k=lfz0vxv&ht=tk&h={host}&f=10954.13456.13457.13458.13459.13460&a=671597&_={_}","w":"lfz0vxv"};
/*{"k":"1.7.0","created":"2014-01-09T10:53:52Z"}*/
;(function(window,document,undefined){
[JAVSCRIPT STUFF...]
})(this,document);

view raw
lfz0vxv.js
hosted with ❤ by GitHub

The next step is to grab the CSS file that’s specified in the JavaScript file (the use.typekit.net address in line 9).

/*
Original
https://use.typekit.net/c/7302fd/1w;freight-sans-pro,1,TJ9:N:i3,TJF:N:i5,TJ8:N:n3,TJB:N:n4,TJD:N:n5,TJG:N:n6/d?3bb2a6e53c9684ffdc9a9bf11e5b2a6273d805f491df729128ca517d0b865e0e191e7b5aee445efc6d4ab4dc94e67aefe35eac8915e2de0959b2bb14cb74eb97243001a4e12199258e040dfe98f737ffac5827d670b2821c337b4c001b82bb67b53127b8ef655ac395a8807eadc56b96dfce3ebaeaf23eda54a42b78fd6598bf206c475067f1648d3f4fcce42c1c8687de1fd8c8d7fdc3934dc65b290046cea982d0a7ac4abb5c8f802f88867e69
*/
/*{"c":"2014-10-30T14:18:23Z","s":"prod-origin-fd43d310","v":"9b86fd"}*/
/*
* The Typekit service used to deliver this font or fonts for use on websites
* is provided by Adobe and is subject to these Terms of Use
* http://www.adobe.com/products/eulas/tou_typekit. For font license
* information, see the list below.
*
* freight-sans-pro:
* – http://typekit.com/eulas/000000000000000000010b59
* – http://typekit.com/eulas/000000000000000000010b5d
*
* (c) 2009-2014 Adobe Systems Incorporated. All Rights Reserved.
*/
@font-face {
font-family:"freight-sans-pro";
src:url(data:font/opentype;base64,[…FONT DATA…]);
font-style:italic;font-weight:300;
}
@font-face {
font-family:"freight-sans-pro";
src:url(data:font/opentype;base64,[…FONT DATA…]);
font-style:italic;font-weight:500;
}

view raw
fonts.css
hosted with ❤ by GitHub

UPDATE: It appears that Typekit has changed the way the CSS file is loaded. You’ll need to use a tool such as Chrome’s network inspector to retrieve the full URL to the file. It will look something like this:

https://use.typekit.net/c/7302fd/1w;freight-sans-pro,1,TJ9:N:i3,TJF:N:i5,TJ8:N:n3,TJB:N:n4,TJD:N:n5,TJG:N:n6/d?3bb2a6e53c9684ffdc9a9bf11e5b2a6273d805f491df729128ca517d0b865e0e191e7b5aee445efc6d4ab4dc94e67aefe35eac8915e2de0959b2bb14cb74eb97243001a4e12199258e040dfe98f737ffac5827d670b2821c337b4c001b82bb67b53127b8ef655ac395a8807eadc56b96dfce3ebaeaf23eda54a42b78fd6598bf206c475067f1648d3f4fcce42c1c8687de1fd8c8d7fdc3934dc65b290046cea982d0a7ac4abb5c8f802f88867e69

Now we’ve got the two files necessary to render the fonts, but we need to do a couple things before they’ll actually work.

First we need to update the HTML snippet to point to our local JS file.

<script type="text/javascript" src="{% static 'offline/typekit.js' %}"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>

view raw
page.html
hosted with ❤ by GitHub

Next edit the JS file so that it points to the local version of the CSS file (specifically, set the following to your local path – “f”:”/offline/fonts/”).

/*
* For font license information, see the CSS file loaded by this JavaScript.
*/
if(!window.Typekit)window.Typekit={};window.Typekit.config={"c":[".tk-freight-sans-pro","\"freight-sans-pro\",\"Helvetica\",\"Arial\",sans-serif"],"f":"/offline/fonts/","fn":["freight-sans-pro",["i3","i5","n3","n4","n5","n6"]],"k":"//use.typekit.net/{id}.js","p":"//p.typekit.net/p.gif?s=1&k=lfz0vxv&ht=tk&h={host}&f=10954.13456.13457.13458.13459.13460&a=671597&_={_}","w":"lfz0vxv"};
/*{"k":"1.7.0","created":"2014-01-09T10:53:52Z"}*/
;(function(window,document,undefined){
[]
})(this,document);

view raw
typekit.js
hosted with ❤ by GitHub

The important thing to note here is that I haven’t pointed directly to the CSS file (i.e. “f”:”/offline/fonts.css”). Unfortunately the JS file adds a slash to that URL based on some regex which prevents it from loading (i.e. /offline/fonts.css/).

So rather than figure out what to edit in the JS, I simply set up a new view in Django to serve the file at a path with a slash at the end (i.e. “f”:”/offline/fonts/”).

from __future__ import absolute_import
from django.views.generic import TemplateView
class FontsView(TemplateView):
template_name = 'offline/fonts.css'
content_type = 'text/css'

view raw
fonts.py
hosted with ❤ by GitHub

With that in place, I can now run the site completely offline without depending on Typekit’s CDN for font delivery.

And one last note, don’t forget to set Typekit to allow your local domain as explained here.