Feb 182011

I have solved the issues using Announcements within the Django admins new formset layout for Announcements.  Announcements will now auto set the author to be request.user when no author is otherwise specified (this happens at the admin.py layer), and Announcements will also auto-set set the pub_date (in the models.py layer) when None is specified.  Initially I thought to add the Validation in the models.py save methods in order to make database guarantees so long as everything modifying the database uses the Django model ORM.  Well this becomes harder to prove when you add tools like PHPMyAdmin, and it turns out that in Django: model validation is better left for clean, and standard practice becomes always calling model.clean() or ModelForm.is_valid() which will in turn drill down into the 3-method clean stack.   We avoid having the validation also in save due to the fact that we would have duplicate queries because standard protocol is to clean your data before saving it.  An extension project such as Johny Caching which caches Django models (with invalidation) could allow developers to return to validating in the clean  AND save methods to make finer tuned database guarantees often sought after more by banking apps that deal with amounts of real money.

Take it a step further and override the appropriate clean methods in the models layer to produce Django compliant model validation code which is somewhat self documenting.  Of course this is more difficult when you wish to tie-in the request.user to auto-fill the author database field (or some similar scenario) since we don’t have access to the request.user at the models.py level we have to move to the admin.py level or manually do this in our view logic.   The only seemingly valid solution requires moving validation of the author field from clean to the save method such that in admin.py you can reach the save_model and save_formset without author field failures and to accept and auto fill-in the request user when no author is specified.  This would also be the correct place to limit the author choices based on his/her role in the system, but we omit this code for now and possibly forever–largely due to the Django Admin’s built in Object history tracking keeping a list and showing the Date/time, User and Action that took place for every object edited in the admin.

Finally then, validating that the author has been filled-in happens during the model save call.   Also since we need to get past clean and into the save_fields (exclude=[‘author’] doesn’t seem to get us there) so we declare the author field to be ( null=True, blank=True ) and then enforce that it isn’t either of those things during save.  Sounds confusing, but that’s why its code.  We define validation this way to help automate the creation of Announcements while still making database guarantees and maintaining the flexibility of an open system.

# Announcement model definition in the student models.py file:
class Announcement(models.Model):
    """ Represents an Announcement in the classcomm system. """
    # Data Model (DB) Fields
    pub_date = models.DateTimeField(blank=True)
    author = models.ForeignKey(User, verbose_name='Author', null=True, blank=True)
    make_global = models.BooleanField('Make Global Announcement?')
    department = models.ForeignKey(Department, verbose_name='Tag Department',
        related_name='announcement_department', null=True, blank=True,
        help_text="Use this field to tag this announcement to a specific department.")
    course = models.ForeignKey(Course, verbose_name='Tag Course',
        related_name='announcement_course', null=True, blank=True,
        help_text="Use this field to tag this announcement to a specific course.")
    headline = models.CharField(max_length=100)
    content = models.TextField('Announcement Markup',
        help_text="Form accepts HTML, but use it mindfully--View display should not be altered!")
    def __unicode__(self):
        return self.headline
    def clean_fields(self, exclude=['author',]):
        """ Implement wrapper for clean_fields to exclude author so we can fill in on save. """
        super(Announcement, self).clean_fields(exclude)
    # EndDef
    def clean(self):
        """ Implement auto fill pub_date. """
        if not self.pub_date:
            self.pub_date = datetime.today()
        super(Announcement, self).clean()
    # EndDef
    def save(self):
        """ Implement auto fill pub_date; Raises ValidationError when missing Author. """
        if not self.author:
            raise ValidationError('New Announcements require an Author be specified!')
        if not self.pub_date:
            self.pub_date = datetime.today()
        super(Announcement, self).save()
    # EndDef
# EndClass
# AnnouncementAdmin definition in the student admin.py file
class AnnouncementAdmin(admin.ModelAdmin):
    """ Admin customizations for Announcement Models. """
    date_hierarchy = 'pub_date'
    list_filter = ['author', 'make_global', 'department', 'course']
    list_display = ['pub_date', 'author', 'headline', 'department', 'course', 'make_global']
    list_display_links = ['pub_date', 'headline']
    list_select_related = True
    search_fields = ['author__username', 'author__email', 'department__name', 'course__name', 'headline']
    fieldsets = (
        (None, {
            'fields': ('headline', 'department', 'course', 'content')
        ('Advanced options', {
            'classes': ('collapse',),
            'fields': ('author', 'pub_date', 'make_global')
    def queryset(self, request):
        """ This query set gets related information for optimized processing.
            We could also do added permissions filtering here ...  """
        return super(AnnouncementAdmin, self).queryset(request).select_related('author', 'department', 'course')
    # EndDef
    def save_model(self, request, obj, form, change):
        """ Autofill in author when blank on save models. """
        if not obj.author:
            obj.author = request.user
        return super(AnnouncementAdmin, self).save_model(request, obj, form, change)
    # EndDef
    def save_formset(self, request, form, formset, change):
        """ Autofill in author when blank on save formsets. """
        instances = formset.save(commit=False)
        for instance in instances:
            if not instance.author:
                instance.author = request.user
        return super(AnnouncementAdmin, self).save_formset(request, form, formset, change)
    # EndDef
# EndClass
Feb 142011

Code is running fairly stable on classcomm dev now, and passing most test.  I’ve released latest Experimental branch code to the public facing server and  configured classcomm.net to point to it. So far the change list on the new version goes something like:

Release: 0.5.8
Merge: Expected from Experimental branch
Date: 2/14/2010
(+) Initial Beta release of Classcomm Instructor.
(+) Refactored gen_sec_link from template sprawl to a subclass of FileField called ProtectedFileField.
This definitely adds a new flavor of stability and elegance to the project.
(+) Now Paginators have configurable default Items per Page views in the settings.py file.
(+) 0.6 requires Django 1.3+ and Python 2.6+
(+) Adds Django Logging default configurations, initial messages and easily demoed packaged with the DISQUS team’s Django Sentry App.
(+) Converts to using Django 1.3 staticfiles
(+) Settings now enables gZip compression for all Communication with browsers that support it, and also enables transactions by default (Transactions have no effect in MySQL – ISAM tables)
(+) Modified site layout to make better use of spacing (more spacing but still mobile compatible views)
Classcomm renders beautifully on test target Augen 7″ touch tablet and passes most page validations.
(+) Fixed issue of ModAuthToken password showing as clear text in on template debug pages when Debug = True by including SECRET in variable name.
(+) Adds duplication prevention Validation (there is a minor race condition on n-parallel systems, but I have begun a short paper on the best approach to solving this in Django across database options with caching enabled. Trying to add a duplicate will render an appropriate form error message!
(+) More sortable tables support—still work to be done on improving sortable for the separator row case.
(+) New dynamically updating JQuery server time clock.
(+) Using the latest Django 1.3 features and admin customizations–initial round of filters and options in place. New product requires Django 1.3
(+) New approach of converting all FileFields to ProtectedFileFields solves the issue of creating accurate working and secure download links in admin—This Now working and this improvement simplifies existing template grammar!
(+) Updates template_loader for the current Django 1.3 release.
(+) Modified student_portal Grade model to include return_date field.
(+) Added dashboard app and made a root page linking to the different app components (this is where the ajax server clock lives).
(+) Adds default 404.html and 500.html error templates.
(+) Adds Custom Validation to the models layer for Grade, Submission and DueDateOverride to explicitly verify Enrollment <–> Assignment Pairing
(+) Adding AJAX function for getting a JSON list of Assignment {id, name}s by Enrollment-id.
(+) Uses previous function in custom template change_form extensions to dynamically filter Assignment Lists based on Enrollment selection.
(+) Better site-wide and code documentation.
(+) Other minor improvements/bug fixes. Please remember we are not supporting widespread use until version 1.0, and the 0.6 release represents many
long standing milestones achievements/bug fixes provided largely in part by the Django 1.3 release. The 0.6.X series will hopefully be exciting indeed!
(***) Current version of Sentry broken in Django 1.3 beta. Remote_url and URL_store path are points of failure in logging when trying to generate page views.

Feb 102011

WordPress has become my number one pick for Blog-rolling a project in the past few years.  It has great support and an ever evolving extensive set of features and plug-ins available.  It is definitely a great software release, and a primary role model for where I want to take Classcomm in the next couple years–well in a way.  

For starters the WordPress interactive install is a great example of an app which is enabled by default and walks through a set of forms configuring what for us is the settings.py file.  WordPress is more than an example however, it does uniquely and precisely what it intends to do just as Classcomm is intended to be a service for delivering and managing Courses on the web.

Besides being able to provide a different set of views and angle on this project, it should also alleviate congestion on the Google Code project hosting homepage, and allow us to keep you better posted on this deploy able (reusable) web application for managing Courses.