Category Archives: Database

SQLite Doesn’t Care About Your max_length

Published / by chris / Leave a Comment

SQLite databases don’t care about your using of max_length on your models. Let’s look at an example.

models.py

class Book(models.Model):
    title = models.CharField(max_length=30)

Now, we’ll run some queries.

$ ./manage.py makemigrations
$ ./manage.py migrate
$ ./manage.py shell
>>> from book.models import Book
>>> title = 'A' * 31
>>> print len(title)
31
>>> book = Book.objects.create(title=title)
>>> book.save()
>>> len(book.title)
31

When running SQL queries using SQLite, you find that you can create records and the database will save a CharField with attributes with bigger strings than what the max_length describes.

Is there any way we can fix this? Sure there is!

One way that we can validate if our model instance is correct is to run a method called full_clean().

So, what we can do is override our save() method so that we can validate our SQLite model before we save our model. Let’s take a look.

class Book(models.Model):
    title = models.CharField(max_length=10)

    def save(self, *args, **kwargs):
        try:
            self.full_clean()
            super(Book, self).save(*args, **kwargs)
        except:
            print "Error saving model"

As you can see, if self.full_clean() throws an error, our instance will print out ‘Error saving model’ and our save method will not save our record to the database.

Try this out next time you use SQLite for your database.

I hope that works for you!

Chris

I have a ChoiceField Callable that Includes a Queryset but it Breaks my Django app

Published / by chris / Leave a Comment

Sometimes it doesn’t make a lot of sense why some things work in Django and others don’t. For example, a ChoiceField has a choices parameter where you can pass in a set of choices and your form will become a select field in your template.

The Django docs says the following:

choices

...an iterable (e.g., a list or tuple) 
   of 2-tuples to use as choices for this field...

Therefore, some people might write the following:

dropdown = forms.ChoiceField(choices=[(1, "Option 1"), (2, "Option 2")])

According to the Django docs, this makes a lot of sense. However, what about if you have a function the returns the same thing? Wouldn’t it might makes sense to write it like this?

def choice_list():
    return [(1, "Option 1"), (2, "Option 2")]

dropdown = forms.ChoiceField(choices=choice_list())

Yes! This will still work. However, what happens if you have a queryset inside of your choices callable? For example, maybe you have code that looks like this:

def get_available_choices():
    choices = ()
    for choice in Choice.objects.all():
        choices = choices + ((choice.data, choice.data),)

    return choices

If you create your form field like this:

dropdown = forms.ChoiceField(choices=get_available_choices())

Suddenly, your app breaks. Everytime you need to do ./manage.py migrate you’ll get errors and unless you know where you look, it’s going to be extremely difficult to find the issue. What do you do? What is going on?

Django looks at your URLs and parses your views.py files before it attempts to create models. It’s literally trying to call a model that doesn’t yet exist before it tries to create the models.

Since Django 1.8, the ChoiceField choices parameter allows you to pass in a callable. Therefore, your app won’t immediately try to call your dynamic choices callable until your models have been created and your form is being initialized.

Therefore, if you have a function like get_available_choices like above, you need to make your field look like this. Notice, all I did was remove the parameters of the function call.

dropdown = forms.ChoiceField(choices=get_available_choices)

Do you have a horrible error when trying to run makemigrations?

$ ./manage.py makemigrations
django.db.utils.OperationalError: no such table: my_app_choice

If you do just try removing the parentheses () from your dynamic choices callable from one of your forms and see if that helps!

Test your Queries before they make it into Production using the Django Shell

Published / by chris / Leave a Comment

You have a database, you have a Django project and a couple third party Django apps that you installed. You create your models, and now you want to try to test a few queries to see if your models are actually going to work.

Where do I start? How can I test to see if my models are going to work? I wish I could access my database to run some SQL queries with Django to see them in action!

Don’t ever write queries in your Django apps before you are sure they are going to do what you want them to do.

What I tend to do, is I open up my Python interpreter. Oh, not just any Python interpreter, but the one that comes with Django.

$ ./manage.py shell

At this point, you can run some Django queries to see what kind of data you will get. This will allow you see what happens when you perform some queries with a lot faster turn over rate. No need to worry about having to create a fake view, fake template, fake url, etc in order to see what query you need to use.

Let’s see an example.

Suppose we have a model called ‘Animal’ and 2 models ‘Cat’ and ‘Dog’ for some Polymorphism Django ORM magic (maybe you have a animal rescue website, so you need to query both Cats and Dogs in the same ‘Animal’ query):

class Animal(models.Model):
    name = models.CharField(max_length=40)
    created_data = models.DateTimeField(auto_now_add=True)

class Dog(Animal):
    house_broken = models.BooleanField(default=False)

class Cat(Animal):
    litter_trained = models.BooleanField(default=False)

Open up your shell using the command above.

Now, you need to import the models:

import pets.models import Animal, Dog, Cat

Now, simply run some queries to see what kind of data you retrieve.

dog1 = Dog.objects.create(name="Fred", house_broken=True)
dog2 = Dog.objects.create(name="Bob")
cat1 = Cat.objects.create(name="Freckles", litter_trained=True)
cat2 = Cat.objects.create(name="Socks")

dog1.save()
dog2.save()
cat1.save()
cat2.save()

animals1 = Animal.objects.all()
animals2 = Animal.objects.filter(created_data__year=2016)

You can do anything you want in the shell. It may take a little longer because you have to write everything out. The great part of this is, you don’t have to create anything other than the models. I created the models in seconds and I wanted to see what these queries would actually do.

Based on what I learned, I’m ready to start a website and start my pet rescue business! 🙂

What do you do when ‘makemigrations’ is telling you About your Lack of Default Value

Published / by chris / Leave a Comment

So you added a few fields on a model. You’re happy about the new model fields and you’re excited to deploy it to your server.

You throw the following into your commandline.

$ ./manage.py makemigrations

Despite your giddiness that people will be working on your new models and you’re going to impress your boss and your friends, you receive the following message:

You are trying to add a non-nullable field 'first_name' to choice without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows)
 2) Quit, and let me add a default in models.py
Select an option:

Oh, no! Now what do you do?

The reason you are getting this prompt is because, your database isn’t empty. The model that you updated with new fields, already has records saved.

Therefore, Django needs to add defaults to those records that already exist.

How do you add a default to your model? How do you add a default to your models?

Let’s say your new model looks like this:

class Subscriber(models.Model):
     first_name = models.CharField(max_length=50)
     # ... the rest of your model ...

How do you add a default to this new field? Quite simply:

class Subscriber(models.Model):
     first_name = models.CharField(max_length=50, default=None)

Then, when you call ‘makemigrations’ again:

$ ./manage.py makemigrations
Migrations for 'subscriber':
  0002_subscriber_first_name.py:
     - Add field firstname to subscriber

Simple! You now have a new field and you got rid of that annoying prompt!

How to filter a DateTimeField by today’s date in Django

Published / by chris / Leave a Comment

Are you getting validation errors when you try to filter a DateTimeField by today’s date?

It can be a huge pain, when, you can’t for the life of you get Django to filter correctly on today’s date.

There is one, simple way of solving this problem.

Once you understand how to do this, you’ll laugh at how easy it was to implement.

Let’s say, for example, that you have a column in a model called post_date.

class MyModel(models.Model):
    post_date = models.DateTimeField(auto_add_now=True)
    # ... additional fields ...

How do you filter DateTimeField by today’s date?

The problem is that you have is that you need today’s posts, but it’s tough because, you’re model is implemented as a DateTimeField. You need to match the actual date. In order to do that you need to match the year, month and day explicitly.

from datetime import date
today = date.today()
today_filter =  MyModel.filter(post_date__year=today.year,
                               post_date__month=today.month,
                               post_date__day=today.day)

But that’s too much typing!

You might think this is way to much to type every time you want want to filter records by date. Are there some things that you can do to make this easier?

Sure! You can create a helper function.

def filter_by_date(date):
    return MyModel.filter(post_date__year=date.year,
                          post_date__month=date.month,
                          post_date__day=date.day)

Or, you can upgrade your instance of Django to 1.9 where there is a filter specifically for dates.

from datetime import date
MyModel.filter(post_date__date=date.today())

The choice is yours! But, now you know exactly what you need to do to filter by date.