Modifying a Django Model
We fulfilled the first user story, but we revealed a weakness in our model. Of course this specific situation could have been prevented. But as a lot of weaknesses are revealed during testing, we have to know how to go back to your model. Lets see the consequences on this simple case.
At this stage, a product owner should add a new task which should be “Submitting duplicate data should be rejected with an informative message“. As we observed in the previous post, duplicate SourceUrls share the same URL. So, lets resolve this issue.
Updating the model
Lets go back to our model. You remember that we set some arguments to the fields. Those are used by Django to generate the SQL and for internal usage. If we should not have two SourceUrl objects with the same URL content, we should just add the option unique. Our model should then be:
from django.db import models class SourceUrl(models.Model): name = models.CharField(max_length = 100) url = models.URLField(max_length = 200, unique = True); lastVisited = models.DateTimeField(auto_now = True) def __unicode__(self): return self.name
Ok, but what are the consequences of this modification ? Save this model and check the content of your database. You still have your duplicate content. Of course, Django have no reason to check the database and delete duplicate content. Try to add a new entry with the URL as http://geekgarage.dad3zero.net/feed/ and see what happens.
With that single argument, Django is able to control your submission of duplicates.
Of course, it does not clean the existing content of the database. We have changed the model and this means that from now, Django will check for duplicate. So we’ll have to clean the database manually.
Have we finished our task here ? Can we submit this to our product owner ? Regarding his request, yes.
Django, a framework for perfectionists…
If you are using Django, you are a perfectionist. But even if perfect is the enemy of good, and our modification is enough from a functional point of view, it is not from a technical point of view. Many developers are not aware of the problems of data and database management. If we shouldn’t be able to submit two sourceUrl objects with the same URL, the database shouldn’t accept to store them. At this stage, you can run an INSERT command and insert a duplicate, because your URL column isn’t unique. What would Django have done ? Run
python manage.py sql reader
You should have
BEGIN; CREATE TABLE "reader_sourceurl" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "url" varchar(200) NOT NULL UNIQUE, "lastVisited" datetime NOT NULL ) ; COMMIT;
Have you noticed at line 5 that the field should now be UNIQUE ? So yes, from the description of the model, Django is able to provide the right SQL query. As we change the model, we have to alter the database. It is useless to run the syncdb command here. Syncdb creates missing tables, but does not alter existing ones. If you change your model and this affects your data structure, you’ll have to execute an SQL alter query yourself.
Working in a team.
There is a legitimate question here. Lets suppose that you are in charge of the application but not the data. The data is designed by an database expert. Of course, you agree on the model and the mapping. Everything works fine except that you didn’t considered that field as unique, but it is in the database. Of course, when you’ll try to add a duplicate, the database will throw an error. How will Django behave ? Lets test it. Just create a database with that constraint but the model without the unique argument. Create a sourceUrl then a second one with the same URL.
As you can see, this is not the same display. Previously, Django failed to validate the form. This time, it encountered an error. This is not really an issue as you are supposed to handle errors. Having an error here may occur for various reasons, the most obvious may be an access timeout. You must handle errors so your users are informed of what happened. With a correct configuration, Django can do some work for you, so be as comprehensive as you can in your model, and consistent with your database.
While we are working on our model, we can improve it:
Should the user provide a name ? We can retrieve a name from the RSS feed, so lets allow the name field to be empty. This is done with the property
But wait, if the name can be empty, what will be displayed for the object in our admin site ? Django calls the __unicode__ function. We must change it so if the name is empty, the URL should be displayed.
from django.db import models class SourceUrl(models.Model): name = models.CharField(max_length = 100, blank = True) url = models.URLField(max_length = 200); lastVisited = models.DateTimeField(auto_now = True) def __unicode__(self): if not self.name: return self.url else: return self.name
Try to add a new entry without providing the name and watch what happens.
A model class is not just a set of fields. A model is a real object with its own functions. You can check the Django documentation for further informations. For now, we’ll go with this one.
So we got back to our model and improved it. We just saw that a change in the model may require to alter the database. So, if we have to change the model of an application already in production, we’ll have to provide the SQL queries for the database.
In the next post, we’ll have work on the page displayed when we connect to our main page.