populate a newly made subclass

This is a bit of a hack but I created a subclass* named FacultyMember and wanted to back-link it with existing records in the Person class: (* This subclass falls under Multi-table inheritance.)

class Person(models.Model):
    lname = models.CharField('Last Name', max_length=75)
    fname = models.CharField('First Name', max_length=75)
    affiliation = models.ForeignKey(PersonAffiliation)
    appointment =  models.ForeignKey(AppointmentType)
    # (20+  other fields)
Newly made subclass.
class FacultyMember(Person):
    research_statement = models.TextField()
    journal_articles = models.ManyToManyField(JournalArticle)
    # (approx 6 fields, several of which are FKs)

(Note, the original FacultyMember model was made 2 years before the Person model. The subclassing is a bit of clean-up.)

There are 581 Person objects, 43 of which are faculty members. It wasn’t obvious to me how to create FacultyMember objects from the existing Person objects. One hack was to do the following.

>>> # (1) retrieve a person who is a faculty member
>>> p = Person.objects.get(lname='Smith', fname='John')
>>> p
>>> <FacultyMember: Smith, John>
>>> # (2) copy the Person object's dictionary
>>> p_dict = p.__dict__.copy()
>>> p_dict
{'grad_program_id': None, 'title_id': 203L, '_state': , 'second_phone': u'',
 'visible': True, 'appointment_id': 22L, 'long_title': u'', 'id': 1607L,
'minitial': u'', 'primary_lab_id': 48L, 'slug': u'john-smith', 'affiliation_id': 11L,
 'room': u'Room 123', 'phone': u'123-456-7890', 'alt_search_term': u'', 'lname': u'Smith',
 'office_id': None, 'second_email': u'', 'fname': u'John', 'grad_year_id': None, 
 'building_id': 1L, 'email': u'jsmith@university.edu'}
>>> # (3) remove the '_state' key/value from the dictionary
>>> p_dict.pop('_state')
>>> # (4) use the dictionary to make/start creating a new FacultyMember
>>> f = FacultyMember(**p_dict)
>>> # (5) link the Person object to the new FacultyMember via the 'ptr' attribute
>>> f.ptr = p
>>> f.save()
>>> f
>>> <FacultyMember: Smith, John>

That’s it.  I made the test above into a short script and it worked.  Initially I tried to instantiate the FacultyMember using the “ptr” attribute or the associated id.  I may try that again…


django urls: list a project’s url names

Below is quick hack to pull out an array of all the url names in a project.

The script iterates through the “urlpatterns” defined in your settings file as the ROOT_URLCONF.

From the “python manage.py shell” prompt:

from django.conf import settings

def load_url_pattern_names(patterns):
    """Retrieve a list of urlpattern names"""
    global URL_NAMES
    for pat in patterns:
        if pat.__class__.__name__ == 'RegexURLResolver':            # load patterns from this RegexURLResolver
        elif pat.__class__.__name__ == 'RegexURLPattern':           # load name from this RegexURLPattern
            if pat.name is not None and pat.name not in URL_NAMES:
                URL_NAMES.append( pat.name)
    return URL_NAMES

root_urlconf = __import__(settings.ROOT_URLCONF)        # access the root urls.py file
print load_url_pattern_names(root_urlconf.urls.urlpatterns)   # access the "urlpatterns" from the ROOT_URLCONF

There’s probably an easier way to this, but I needed the url names for an error checking script.

Django Admin: Resizing Form Fields (for TabularInline)

Recently, one “small”* project in particular required the resizing of fields for displaying Inline Models in the admin.

For example, one inline model, “ElectroporationConditionsRecord” needed to display 11 fields.

(click image to see it full sized)

Squeezing the 11 fields into a readable row meant updating the “size” attribute of each input box.  A “default sized” input box in the admin does not include the “size” attribute, as in ‘size=”7″‘ below:

<input type=”text” size=”7” value=”0″ name=”id_days_of_drug_selection” id=”id_days_of_drug_selection”>

One way to add the “size” attribute, or any other attribute, is by making a form and connecting it to the admin.  For this example, three files are involved in making this happen:

my_appp/models.py      # definition of model ElectroporationConditionsRecord

(1) models.py

Here is a snippet of the ElectroporationConditionsRecord model, from the models.py file:

class ElectroporationConditions(models.Model):

    example_target_model = models.ForeignKey(ExampleTargetModel)  # example FK
    construct_name = models.CharField(max_length=40)
    drug_selection = models.CharField(max_length=40)
    days_of_drug_selection= models.IntegerField(default=0)

(2) forms.py

The inline model for the ElectroporationConditionsRecord in the screenshot above uses the following form, located in the forms.py file:

from django import forms

class ElectroporationConditionsForm(forms.ModelForm):
    '''ElectroporationConditionsForm.  Used to size the text input boxes'''

    class Meta:
        widgets = { 'construct_name': forms.TextInput(attrs={'size': 20})
                    , 'drug_selection': forms.TextInput(attrs={'size': 20})
                    , 'days_of_drug_selection': forms.TextInput(attrs={'size': 7})
                    , 'drug_concentration': forms.TextInput(attrs={'size': 7}) 

                    , 'dna_quantity': forms.TextInput(attrs={'size': 7})
                    , 'dna_concentration': forms.TextInput(attrs={'size': 7})
                    , 'linearized_by': forms.TextInput(attrs={'size': 7}) 

                    , 'passage': forms.TextInput(attrs={'size': 7})
                    , 'peak_voltage': forms.TextInput(attrs={'size': 7})
                    , 'time_constant': forms.TextInput(attrs={'size': 7})
# examples of other form widgets: PasswordInput, HiddenInput, Select, DateInput, etc.

Note, each key in the widgets dictionary above (e.g. ‘construct_name‘, drug_selection‘, etc.) is the name of a field in the model ElectroporationConditionsRecord.

You could also substitute the “size” attribute for a css class, as in:

              , 'time_constant': forms.TextInput(attrs={'class': 'input_sm_number'})

where the css might be something like:

             input.input_sm_number { width:6opx; }

(3) admin.py

To connect the new ElectroporationConditionsForm to the model ElectroporationConditionsRecord, a change is made in the admin.py:

# For this TabularInline Admin model, use the form that sets the size attributes
class ElectroporationConditionsAdminInline(admin.TabularInline):
    model = ElectroporationConditionsRecord   # from models.py
    form = ElectroporationConditionsForm      # from forms.py, sets the size attributes for the input boxes

# example of using the TabularInline Admin model defined above
class ExampleTargetModel(admin.ModelAdmin):
    # note: the ElectroporationConditionsRecord has an FK to this model, ExampleTargetModel
    inlines = (  ElectroporationConditionsAdminInline,)

Basically, that’s it.

In summary, to define the field width  in the admin model:

(a) Make a form similar to the ElectroporationConditionsForm, as in step (2) above.

(b) In your admin.py file, connect the form (ElectroporationConditionsForm) to your model, similar to the ElectroporationConditionsAdminInline model shown in step (3) above.

There are also other ways to do this, as seen on StackOverflow.com.

* The project is still small/low maintenance meaning it’s just models and the Django admin. It has 39 database tables, not including the “built-in” django tables.

Highcharts in the Django Admin

(Note, this assumes that you have a Django project installed and running. This example is for Django 1.3, the official version.)

Below are some notes for adding Highcharts to a Django admin page. In this case, I want to chart the enrollment in a science class over time.

Specifically, I have a model name “Course” and I want to see the chart on the “Course” change form in the admin.

(1) Download Highcharts, unzip it, and place in a directory under your Django MEDIA_ROOT directory, as specified in your Django settings.py.

(2) Make a blank template file named “change_form.html” file as well as the appropriate directories for your model. In the case of my Course model, the location is:


Notes on the path above:

“templates” – a directory specified in your django settings.py file

“admin” – indicates a template for the django admin

” course” – name of my app. (The Course model is defined in ‘../course/models.py’)

“course” – name of the model, Course, in lowercase

change_form.html” – name of an existing file in the django source code (/django/contrib/admin/templates/admin/change_form.html)

(2) Add the following lines of code to your change_form.html file.

{% extends "admin/change_form.html" %}
{% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../jsi18n/" }}"></script>
{{ media }}
<!-- start: two new lines for highcharts -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>
<script src="{{ MEDIA_URL }}highcharts/js/highcharts.js" type="text/javascript"></script>
<!-- end: two new lines for highcharts -->
{% endblock %}

Most of the code above is taken from the “extrahead” block in the original “change_form.html” file. Two new lines of javascript have been added, as indicated by the comments above.  (Again, this is Django 1.3, the official version. If you’re using a different version, copy the appropriate code from the change_form.html template included with django.)

The 1st line calls in the JQuery library and the 2nd line connects to highcharts.js.

(3) Test. Go to the the Django admin and attempt to add a new instance of your model, or edit an existing one. View the source in your browser:

(a) Make sure that the new lines show up and that

(b) the javascript files are being loaded. (e.g., Click on the links to the jquery and highcharts js files to make sure they load.)

Note: Django 1.3 has JQuery built-in but it has it’s own namespace. Using the built-in JQuery requires editing highcharts.js and changing “JQuery” to “django.Jquery”.  Because of maintenance, changing the highcharts.js is not advisable.

(4) For my Course model, I’m adding a function in the models.py file named “enrollment_chart”.  For example purposes, I’m throwing test data into the function.  (In reality, the function calls a queryset that is passed to the template.)

    def enrollment_chart(self):
        lu = { 'categories' : [ 'Fall 2008', 'Spring 2009','Fall 2009', 'Spring 2010', 'Fall 2010', 'Spring 2011'],\
             'undergrad' : [18, 22, 30, 34, 40, 47],\
             'grad' : [1, 2, 4, 4, 5, 7],\
             'employee' : [2, 3, 0, 1, 1, 2] }
        lu['total_enrolled'] = [sum(a) for a in zip(lu['undergrad'], lu['grad'],lu['employee'])]

        return render_to_string('admin/course/course/enrollment_chart.html', lu )
    enrollment_chart.allow_tags = True

(5) Make the “enrollment_chart.html” file referred to in the function above.  Note, the file goes in the same directory as the “change_form.html” file in step (2).

(6) In the “enrollment_chart.html” file, add the following code:

Note 1:  This code is from the highcharts example graph with some changes to the labels and the addition of Django template tags.

Note 2: the “renderTo” attribute must match the “id” of the <div ..> at the bottom of this code.

<script type="text/javascript">
var chart;
jQuery(document).ready(function() {
	chart = new Highcharts.Chart({
		chart: {
			renderTo: 'enrollment_container',
			defaultSeriesType: 'line',
			marginRight: 130,
			marginBottom: 30
		title: {
			text: 'Enrollment by Semester ',
			x: -20 //center
		subtitle: {
			text: '',
			x: -20
		xAxis: {
			categories: [ '{{ categories|join:"','" }}']
		//	categories: ['Fall 2008', 'Spring 2009','Fall 2009', 'Spring 2010', 'Fall 2010', 'Spring 2011']
		yAxis: {
			title: {
				text: 'Number of Students'
			plotLines: [{
				value: 0,
				width: 1,
				color: '#808080'
		tooltip: {
			formatter: function() {
	                return ''+ this.series.name +''+this.x +': '+ this.y;
		legend: {
			layout: 'vertical',
			align: 'right',
			verticalAlign: 'top',
			x: -10,
			y: 100,
			borderWidth: 0
		series: [{
			name: 'Total Enrolled',
			//data: [1, 27, 34, 39, 46, 56]
			data: [{{ total_enrolled|join:"," }}]
		}, {
			name: 'Undergrads',
            data: [{{ undergrad|join:"," }}]
			//data: [18, 22, 30, 34, 40, 47]
		}, {
			name: 'Grads',
			data: [{{ grad|join:"," }}]
			//data: [1, 2, 4, 4, 5, 7]
		}, {
			name: 'Employees',
			data: [{{ employee|join:"," }}]
			//data: [2, 3, 0, 1, 1, 2]

<div id="enrollment_container" style="width: 700px;height: 500px"></div>

(7) Update your admin.py file to connect the enrollment_chart() function in step (4) to your admin screen.

In the admin.py file, add enrollment_chart to:

(a) the list of readonly_fields, as well as

(b) a field in your fieldsets array

(Location of the admin.py file for the Course model: ../course/admin.py)

Below is an example where enrollment_chart has been added:

class CourseAdmin(admin.ModelAdmin):
    list_display = ( 'course_id', 'title', 'catalog_number', )
    search_fields = ( 'course_id', 'title', 'catalog_number', )
    list_filter = ('status',)

    readonly_fields = ('semester_details', 'enrollment_chart' )
    fieldsets = [
     ('Course', { 'fields':  [  'course_id', 'title', 'catalog_number', \
                    'department', 'course_type', 'status', ]}), \

    ('Semester Details', { 'fields':  [  'semester_details',  ]}),\
    ('Enrollment', { 'fields':  [  'enrollment_chart',  ]}),\
admin.site.register(Course, CourseAdmin)


The explanation above is long, but, if you’re django instance is already running, the steps are fairly quick.

(a) Download Highcharts, unzip it, and place in a directory under your Django MEDIA_ROOT directory (step 1)

(b) Add two admin templates:

change_form.html  – call the JQuery and the highcharts.js libraries  (steps 2 and 3)

enrollment_chart.html – or another template to put the chart’s javascript + containing <div..> (steps 5 and 6)

(c) Create a function accessible by your model that passes chart data  (step 4)

(d) Modify the admin.py to link the function to the django admin and a template (step 7)

Once the basics are going, you can start customizing your chart:)