Skip to content

Django notes

StephenChan edited this page Oct 10, 2012 · 10 revisions

This page is for notes about Django that are relevant to CoralNet development. This page isn't meant to be a re-wording of the entire Django documentation; its main purpose is to emphasize certain CoralNet-relevant points while offering some additional tips and links. However, in theory, a Django expert could skip this page entirely and just read the CoralNet documentation.

Contents

Media and static files

General info: media, static files

Media

Media can be a very general and confusing term. Here, we'll discuss only the kind of media that MEDIA_ROOT and MEDIA_URL refer to.

Media are user-uploaded files (such as images) or files that are generated from user-uploaded files (such as image thumbnails). In general, if you have a development copy and a production copy of a website, the media are going to differ between the two. This is how media differ from source code files and static files.

Media should be uploaded or generated in the settings.MEDIA_ROOT directory. Since media are never going to be committed to the Git repository, settings.MEDIA_ROOT may be located outside of the repository (but it currently isn't in CoralNet, as of 2012 Oct 9).

Static Files

Static files are files that:

  • Are loaded directly in the user's web browser (unlike server-side code, such as the Python .py files).
  • Remain the same between development and production copies (unlike media).

This includes CSS (Cascading Style Sheet) files, JS (JavaScript) files, and image files used for the site design, icons, etc.

Static files can be put in either of two places:

  • The /static subdirectory of any of our installed apps.
  • Any directory listed in settings.STATICFILES_DIRS (this is mainly used for static files that aren't tied to a particular app).

In order to actually serve static files to users, the static files need to be collected (i.e. copied over) to one place first. This is done differently depending on the value of settings.DEBUG.

  • DEBUG == True: static files are automagically collected and served.
  • DEBUG == False: static files must be manually collected in one place. To do this, first set settings.STATIC_ROOT to the directory where you want to collect the static files (this directory can be outside of the repository). Then run the command python manage.py collectstatic to collect the static files in STATIC_ROOT. You must do this again every time a static file is updated or added.

(TODO: Information about preventing caching of static files)

Unit tests

See CoralNet documentation - Unit tests for additional CoralNet-specific info.

Basic resources

  • Django docs - General info, how to write tests, how to run tests, Django-specific tools, Django-specific assert statements
  • Python's unittest docs - info about Python unit tests (not specific to Django or web frameworks), general unit test assert statements
  • Sample advice on what kind of functionality should be tested: 1 2

Requirements

  • Ensure that the Django MySQL user has all privileges on the test database. For more info, see Setup - MySQL - Create a database and a user.
  • By default, settings.DEBUG is set to False for unit tests, even if our settings file specifies it as True. Thus, if you haven't already done so, you should make sure your development copy can run the server with settings.DEBUG == False. This includes making sure static files are collected at settings.STATIC_ROOT; if this is not done, then running any test that uses the Test Client may result in static-file-related errors (such as The system cannot find the path specified: u'css/master.css').

Running unit tests

  • manage.py test - Runs all tests of all apps in settings.INSTALLED_APPS, including third-party apps. Note that running third-party apps' tests is mainly only done to confirm compatibility and correct installation of these apps. In the normal workflow, it is typically only necessary to run tests for the apps we wrote.
  • manage.py test [appname[.classname[.methodname]]] - Runs only tests of a specific app, or a specific test class, or a specific test method. Examples: manage.py test images, manage.py test images.ImageUploadTest, manage.py test images.ImageUploadTest.test_image_upload_success
  • If you use PyCharm, you can do the equivalent of manage.py test [appname[.classname[.methodname]]] by right-clicking a test module, class, or method, and then selecting Run 'Test: ...' in the context menu.

Tip: If you're changing the project code, and you find that a test is failing when it didn't before, it can indicate either of two things: (1) You broke some functionality. (2) The functionality has changed and the test needs to be changed to reflect that.

Writing unit tests

A test class inherits from unittest.TestCase (or a subclass of it, like django.test.TestCase, or BaseTest and ClientTest in lib/test_utils.py).

A single test is a test method within a test class. To specify a test class's test methods, either define a test suite for the class, or don't define a suite and simply prefix each test method's name with test. Read here for more info.

You can specify initial database data for tests by providing fixtures.

  • When writing fixtures, try to avoid hard-coding object IDs: specify the pk field as null and use natural keys. This can't always be done, however; for example, IDs will need to be hard-coded for Users and Groups until this Django ticket is accepted and incorporated into a Django release.
  • Watch out for [[circular foreign key references in MySQL's InnoDB|http://www.mail-archive.com/[email protected]/msg66968.html]], which is the database engine we use.
  • Fixtures can only specify initial database data, not initial files in the filesystem (like initial media, image processing files, etc.). There is no scheme for specifying initial files. (However, CoralNet has a directory for sample files that may be uploaded during tests: settings.SAMPLE_UPLOADABLES_ROOT.)

Tip: Maintain meaningful names for test classes and methods, and whenever possible, maintain large numbers of small test methods instead of small numbers of large test methods. That way, if a test fails, it's easier to tell what kind of test failed.

Clone this wiki locally