Sharpe's Django Blog

utorok 6. októbra 2009

Django Tricks

Another interesting blog about Django: http://djangotricks.blogspot.com/.

nedeľa 13. septembra 2009

Long model names in Django

IMPORTANT UPDATE: Probably better solution, than extending varchar sizes as described below, is not allow long verbose names to exceed particular varchar size. It does not mean, that you can't have long verbose names! You only need to do small trick: when adding new models, do not populate verbose names! You can do it after first syncdb, but default string values created by first syncdb in auth_permission and django_content_type won't be replaced! Do following not till this is not sufficient.

UPDATE: Attribute codename is probably not an issue.


There is a limitation in Django - when using long model names (class names, attribute names, multiple inheritations, long verbose names, ...), you may hit a snag, when running python manage.py syncdb:

psycopg2.DataError: value too long for type character varying(50)
or
psycopg2.DataError: value too long for type character varying(100)

The root cause is in two automatically generated fields in auth_permission table: name (varchar(50) by default) and codename (varchar(100) by default).

This issue is described for example here:
http://groups.google.com/group/django-users/browse_thread/thread/964fdef3e374e35c
http://www.mail-archive.com/django-users@googlegroups.com/msg73888.html

This behaviour of Django probably won't be changed, due to potential backwards incompatibility, so if your project is generating long model names and you will hit error messages above, do not suffer, and extend varchar maximum length for name and codename in table auth_permission.

I've changed both to varchar(200) by pgAdmin and after following syncdb error message disappeared (for now ;), but it is possible to do it also by SQL command (e.g. ALTER TABLE ...).

UPDATE: Another candidate for extension is attribute name in table django_content_type, and maybe other attributes in other tables, when you'll get that error message, just play with it and you will find out what to extend.

pondelok 7. septembra 2009

How to set multi-field primary key

I have class AccountingDocument and want to have combination of Type, Issuer and Identifier unique. Howto is here.

The implementation:
class AccountingDocument(models.Model):
type = models.ForeignKey('AccountingDocumentType', help_text='Typ účtovného dokladu.')
identifier = models.CharField(max_length=40, help_text='Číslo účtovného dokladu, jedinečný identifikátor, ktorý mu jeho vystavovateľ pridelil, napr. číslo faktúry.')
  date_issued = models.DateField()
  time_issued = models.TimeField(null=True, blank=True)
  currency = models.ForeignKey('Currency')
  value_without_VAT = models.FloatField(help_text='Suma účtovného dokladu bez DPH.')
  VAT_rate = models.FloatField(default=19, help_text='Sadzba DPH.')
issuer = models.ForeignKey('BusinessUnit', related_name='AD_issuer')
  recipient = models.ForeignKey('BusinessUnit', null=True, blank=True, related_name='AD_recipient')
  scanned_items_file = models.FileField(null=True, blank=True, upload_to=settings.ACCOUNTING_DOCUMENTS_FILE_PATH)
class Meta:
unique_together = ("type", "identifier", "issuer")
# This is a list of lists of fields that must be unique when considered together.
# It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement).
# For convenience, unique_together can be a single list when dealing with a single set of fields.
That's it, no evolution needed, just run python manage.py syncdb in project directory.

nedeľa 6. septembra 2009

Uploading files via Django application

There are two field types in Django for uploading files:
  • FileField (basic field type for any file type), see FileField reference
  • ImageField (advanced variant of FileField, able to do some additional operations useful for images), see ImageField reference
There is also third type - FilePathField, but this cannot be used for uploading, only for selecting already uploaded files, so it won't be covered here.

End of theory, let's look, how to use it in practise.

Environment prepared was documented in previous article: Django + mod_wsgi + Apache2 + PostgreSQL. Following changes need to be done:

settings.py


# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = '/home/django/DSBIS/media/'

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = 'http://localhost/DSBIS-media/'

# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/admin-media/'

...

# application specific settings:
ACCOUNTING_DOCUMENTS_FILE_PATH = 'accounting_documents/'


core/models.py

class AccountingDocument(models.Model):
...
scanned_items_image = models.ImageField(null=True, blank=True, upload_to=settings.ACCOUNTING_DOCUMENTS_FILE_PATH)
scanned_items_file = models.FileField(null=True, blank=True, upload_to=settings.ACCOUNTING_DOCUMENTS_FILE_PATH)

scanned_items_file_path = models.
FilePathField(null=True, blank=True, path=settings.ACCOUNTING_DOCUMENTS_FILE_PATH)

...


core/admin.py

...
admin.site.register(AccountingDocument)
admin.site.register(AccountingDocumentType)
...

urls.py

  ...
# Uncomment the next line to enable the admin:
(r'^admin/(.*)', admin.site.root),
...

/etc/apache2/conf.d/security

AliasMatch /([^/]*\.css) /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/css/$1

# to be able serve images for admin site (backslash at the end of next line is important):
Alias /admin-media/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/
<Directory /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media>
Order deny,allow
Allow from all
</Directory>

# to be able serve images for DSBIS application:
Alias /DSBIS-media/ /home/django/DSBIS/media/
<Directory /home/django/DSBIS/media>
Order deny,allow
Allow from all
</Directory>

WSGIScriptAlias / /home/django/DSBIS/apache/django.wsgi

<Directory /home/django/DSBIS/apache>
Order allow,deny
Allow from all
</Directory>
Do not forget restart Apache after those changes.
sudo /etc/init.d/apache2 restart
If user under which Apache runs is not allowed to write to destination directory, fix it (e.g. by using chown, chgrp or chmod).


Now you can test it.
Log into admin site and try to upload file by saving "Accounting Document" record.


Deployment of the Django project using Apache2 and mod_wsgi on Ubuntu Linux

After several hours of pain when trying to have Apache+mod_wsgi+Django working on Mac OS X (Leopard), I decided to try Ubuntu Linux 9.04.

Prerequisities installed:

Python is installed by default, Django installation is pretty straightforward, just follow its official documentation. To see, how to install psycopg2, look at this how to. PostgreSQL installation is also pretty straightforward.


The installation itself:

sudo apt-get install libapache2-mod-wsgi

This command will install not only mod_wsgi, but also Apache2 webserver.


Webserver configuration:

Starting point:
http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/

What does that mean in shortness?
  1. Create apache/django.wsgi in your project directory as recommended.
  2. Add line with WSGIScriptAlias (for Django code) and AliasMatch (for CSS) directive into /etc/apache2/conf.d/security (included from /etc/apache2/apache2.conf) pointing to your apache/django.wsgi.
  3. Restart webserver: sudo /etc/init.d/apache2 restart.
  4. Test it accessing localhost via HTTP (http://localhost/admin or http://localhost).
What is my exact configuration? (example)

Contents of /home/django/DSBIS/apache/django.wsgi:


import os
import sys
os.environ['DJANGO_SETTINGS_MODULE'] = 'DSBIS.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
sys.path.append('/home/django')


Contents of /etc/apache2/conf.d/security:



It's working.

Further reading:
http://docs.djangoproject.com/en/dev/howto/deployment/
http://djangobook.com/en/2.0/chapter12/

NOTE: This configuration of Apache is not final, I did further changes, but it will be subject of another article on this blog.

sobota 5. septembra 2009

How to reset Django Evolution of the project

Let's imagine the situation:

You have got into deadlock in Django Evolution and you cannot evolve forward.

You don't need the history of your evolutions (e.g. you use Subversion or other version control system, or you don't need history at all).

This was my case, I added some attributes to model, then installed Django Evolution, added 'django_evolution' to INSTALLED_APPS of my project, and ran 'python manage.py syncdb'. Problem was, that I did changes to model before those syncdb, and the problem appeared, when trying to remove that newly added attributes: Error applying evolution: column "title_before" of relation "core_person" does not exist.

Problem was, that Django Evolution thought, that those attributes existed, but they were not created in the database.

Maybe there is smarter solution, but this worked:

  1. Rollback your changes to model manually (e.g. comment newly added lines - attributes).

  2. Issue these commands in your project's directory:

    python manage.py reset django_evolution

    python manage.py syncdb

  3. Now you can do the changes again (or do other changes) and evolve issuing this command in your project's directory:

    python manage.py evolve --hint --sql
    python manage.py evolve --hint --execute
Enjoy.

Update: other case, where this procedure is useful, is manual modification of database, not via Django Evolution. I was forced to do it also in this case.

Archív blogu