In Django, Effectively Allowing Addition and Removal of Related Models

I’m very new with Django and am happy to accept criticism or suggestions on better ways to do this.

The use case for what I’ve done is this: I’m working on a database to track membership for a small political committee. One of the things we’re interested in is the districts in which our members live. There are different types of districts that we want to track including voting precincts, municipal boroughs, state house districts, state senate districts, and U.S. congressional districts.

For creating or editing a person’s information, there should be a single select box for each type of district. But I want admins to be able to add and remove district types through the admin panel.

To make this work, I have four models: Person, District, DistrictType, and Residency. Residency is an intermediate model for Person and District, but I defined it as a class instead of letting Django create it.

Here is a very simplified version of my models:
Models.py

class DistrictType(models.Model):
    name = models.CharField('Name', max_length=30, help_text='The name of the type of district')

    def __str__(self):
        return self.name


class District(models.Model):
    name = models.CharField('Name', max_length=30, help_text='The name of the type of district')
    district_type = models.ForeignKey(DistrictType, on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.name

class Person(models.Model):
    name=models.CharField('Name', max_length=30, help_text='The person's full name'

    def__str__(self):
        Return self.name

class Residency(models.Model):
    district = models.ForeignKey(District, on_delete=models.CASCADE, help_text='The district in which the person lives')
    person = models.ForeignKey(Person, on_delete=models.CASCADE, help_text='The person who lives in the district')

    def __str__(self):
        return self.district + ': ' + self.person

 

I want admin to be able to add and remove districts and district types.

admin.py

from .models import District, DistrictType

class DistrictAdmin(admin.ModelAdmin):
    list_display = ( "name", "district_type")
admin.site.register(District, DistrictAdmin)

class DistrictTypeAdmin(admin.ModelAdmin):
    list_display = ( "name")
admin.site.register(DistrictType, DistrictTypeAdmin)

 

With District and DistricType registered, admins can add and remove district types (and districts) as needed.

I want users to be able to add and edit people without using the admin panel, and I want the person form to have one select box for each type of district. Of course, I have permissions set to restrict who can add and edit people, but I’m not covering that here.

If, for example, I have four types of districts, I want four select boxes on the person form. Each of those select boxes represents a residency. So I have to define a residency form to be used as an inline model form.

forms.py

class PersonForm(ModelForm):
    class Meta:
        model = Person
        fields = (
            "name",
        )

class ResidencyForm(ModelForm):
    district_type_name = forms.CharField(required=False)
    class Meta:
        model = Residency
        fields = (
            "district",
        )

 

Note that district_type_name is not part of the residency model and won’t be used in the update or creation of the object. The reason it is there will be explained below.

The residency model has two fields, person and district, but only district is needed here because person will be taken care of by the formset factory.

views.py

class PersonCreate(CreateView):
    model = Person
    form_class=PersonForm

    district_type_all = DistrictType.objects.all()
    district_type_count = DistrictType.objects.count()

    ResidencyFormSet = inlineformset_factory(Person, Residency, form=ResidencyForm, extra=district_type_count, max_num=district_type_count, fields='__all__')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        residencies = self.ResidencyFormSet( instance = self.object )
        for i in range( self.district_type_count):
            residencies.forms[i].fields['district'].queryset = District.objects.filter(district_type__id=self.district_type_all[i].id)
            residencies.forms[i].fields['district_type_name'].initial = self.district_type_all[i].name
            context['district_label_' + str(i)] = self.district_type_all[i].name
        context['residencies'] = residencies

        return context

    def form_valid(self, form):

        self.object = form.save()

        context = self.get_context_data()

        residencies = context['residencies']
        if residencies.is_valid():
            residencies.save()

        return super(PersonCreate, self).form_valid(form)

 

If you don’t understand inline formsets, I think Daniel Chen’s post will explain their use a lot better than the Django docs.

With the line

ResidencyFormSet = inlineformset_factory(Person, Residency, form=ResidencyForm, extra=district_type_count, max_num=district_type_count, fields='all')

, I created a set of inline forms. The first parameter, Person, defines the parent model. This is why I don’t need a person field in my form definition. The extra parameter defines how many inline forms to display. I want that number to match the amount of district types.

The max_num parameter ensures there will be no more forms displayed than the amount of district types. It’s not necessary in the creation view, although I included it here, but it is necessary in the update view. Without it, there would be four extra select boxes in addition to those that already have data from the previous update or creation.

At this point, I would have four identical inline forms, each with a select box that includes every district in the database. But I want to limit each select box to a certain district type. This is where I think I’m breaking new ground.

Here’s another look at get_context_data in views.py

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        residencies = self.ResidencyFormSet( instance = self.object )
        for i in range( self.district_type_count):
            residencies.forms[i].fields['district'].queryset = District.objects.filter(district_type__id=self.district_type_all[i].id)
            residencies.forms[i].fields['district_type_name'].initial = self.district_type_all[i].name
        context['residencies'] = residencies

        return context

 

In get_context_data, I go through each of the residency forms in the formset, and update the queryset for it’s select box. Now each select box is limited to districts of a certain district type.

Here’s where that extra district_type_name field comes in. The default label for the district field is “District”. But on the form, I want each label to be the name of the district type that the select box is limited to. So I populate the unbound field district_type_name with the name of the district type, and use that value as the label.

From person_form.html in my templates directory


    {% csrf_token %}
    <div class="section" >
        Name
        <div class="row" >
            <div class="label" >
                Full Name
            </div >
            <div class="fields" >
                <div class="field" >
                    {{ form.given_name }}
                </div >
            </div >
        </div >
    </div >

    {{ residencies.management_form }}
    <div class="section" >
        Districts
        {% for form in residencies.forms %}
            <div class="row" >
                <div class="label" >
                    {{ form.district_type_name.value }}
                </div >
                <div class="fields" >
                    <div class="field" >
                        {{ form.district }}
                    </div >
                </div >
            </div >
        {% endfor %}
    </div >

    

Here I used district_type_name.value as the label for each select box

Redirecting a Request with Path Information

bnmng.com is mapped to my share on totalchoicehosting‘s servers. But users can still type bnmng.com in the address bar and get redirected to the WordPress blog along with any extra path information. So http://bnmng.com/category/computing/ becomes http://bnmng.wordpress.com/category/computing.

I do this with a simple index.php in the home directory:
index.php
<?php
header('LOCATION: http://bnmng.wordpress.com' . $_SERVER['REQUEST_URI']);
?>

$_SERVER[‘REQUEST_URI’] is everything after the .com. In case the code wraps on your screen, this is just one line of code between the <?php and ?> tags.

But this wouldn’t work unless I ensured that my index.php file would respond to the url with the path info.

To do that, I copied WordPress.org‘s .htaccess file (described here as “genius”, and explained in more detail here), which is found on self-hosted WordPress blogs. This requires Apache’s mod_rewrite.

.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php  [L]
</IfModule>

Without this, http://bnmng.com/category/computing/ would simply return a “page not found” error.

That’s all I need to make the redirect. The redirect only occurs if the request is bnmng.com without any path information or bnmng.com with path information that doesn’t correspond to anything on my share. http://bnmng.com/codeexamples, for example, doesn’t get redirected.

This also seems to work for any bnmng.com feeds that people may have subscribed to before I moved bnmng.com from wordpress.com to totalcoicehosting.com. There’s no need to create a feed directory on totalchoicehosting, as I described in an earlier post.

I could have just put a self-hosted blog in my home directory and had a smoother effect without the redirect, but I like the convenience of using WordPress.com’s servers. And if they make a little money with ads on my blog then I’m happy to support them, especially if it doesn’t cost me a dime.

Blog Reader / Post Sharer

I’ve been experimenting a way to share posts on my sidebar. Rather than a static blogroll which just lists blog titles or an RSS feed which shows every post from the feed, I want to specifically select the posts that appear on the sidebar. I don’t know if there’s already something out there that does this.

I’m working on a blog reader which lets me select posts. When I choose to share a post, it writes an XML file which includes only those selected posts.

On my blog, I have an RSS widget which reads the file that I created.

My source is a yahoo pipe:

I use the regex module to add a feed title to the beginning of each post. Doing this is useful for a mashup because the feed title would otherwise get lost.

But the source can be any valid feed, either a mashup or a feed from a single blog.

What my reader does is displays all posts from the source feed, each with a “Share” button, which, when pressed, recreates my XML file. The XML file is an RSS feed which only includes posts which I have selected. It doesn’t include posts which have scrolled off my feed, so this isn’t like a bookmarking service which will save bookmarks indefinitely.

The result is on my sidebar, titled “Selected Posts from my Blogroll”.

It only updates when I use it, so my sidebar will be stale if I don’t keep up on it.

The server that this all runs on is at suffolkian.com. My xml output file is http://www.suffolkian.com/bnmng_blogroll/selectposts/selectedposts_bnmng.xml

I’m still just experimenting, but if you want to use this application or help me test it, use the contact form and let me know your user name, email address, desired password, and source feed. This is real low security, so you probably shouldn’t use the same password that you use for your online banking. I’ll make a configuration file for you and email the link to your reader and the link to the xml file that the reader generates. I make no promises that it will work or that you won’t loose your saved posts as I make changes to the code.

Feed Combining: Yahoo Pipes / RSS In Page

On Tina’s old blog, which is still online but not updated since she moved to WordPress, there is a blog roll on her sidebar which shows her blogs in order of most recent update. I can’t find anything that works as well for WordPress. There are various feed combiners for WordPress, but all that I found leave out the name of the blog that the item comes from. And I wanted that to be included.

The best way I found was to use Yahoo Pipes, an online feed combiner from Yahoo. I use the RSS output from Pipes as the source for WordPress’s standard RSS module. Pipes is difficult to understand at first, but it has a video tutorial that should be enough to get a novice started. More advanced operations, like what I’m about to describe, are for people who are more experienced with internet applications.

What I like best about Pipes is the Regex module, which allows me to alter the feed of each item before it gets combined with other feeds. I use the Regex module to put an identifier in the title of each item.

The Feed module gets piped to the Regex module. In the Regex module I select item.title, and replace ^ with the name I want to use followed by a colon and a space. So “The Apocalypse Trade” becomes “Krugman: The Apocalypse Trade”. (^ is Regular Expression syntax for the beginning of a string).

I pipe all of the Regex-modified feeds into a Union module, then pipe the Union module into a Sort module. Finally, the sort gets piped to the Pipe Output. An example of the output is in my main blog.

Yahoo Pipes Editor

To get the RSS feed, I click “Back to My Pipes” and select the pipe I want, then click “View Results”, then “Get as RSS”.

The Regex module has much more capabilities than what I’m using it for and Pipes has more modules than I’ve explored.

Gigaom has a more in depth article.

The interface is a little hard to get use to and Yahoo Pipes intermittently fails to retrieve a feed, but it’s power and versatility make up for any shortcomings.

For a tool that works in wordpress.org and doesn’t require an external combiner, use Rss in page. RSS in Page does not provide a new feed, but it will embed the output of a combined feed into a page or text widget. There’s no graphical interface but if you’re familiar with using shortcodes and can learn some simple formatting commands it’s very versatile.

An improvement, and another use for the safetags function

A while ago I wrote a function which checks HTML snippets and ensures that tags which should be closed are removed unless they are actually closed. For example, if user types

<div style="whatever">Blah Blah

and forgets to close the div tag, then that div tag will be removed. Sure it would be better if the div tag was fixed, but that requires more code. This is good enough for me.

Another use for this function is excerpts. An original article may have all the tags properly closed but the excerpt might have an opening tag with it’s matching tag cut off.

A copy of the function is here.

It is not a validator. It ignores tags like p and br which will work even if not coded properly. You can edit the function to change which tags are allowed.

I used the function to alter Virginia CURE’s website from the original cover-wp theme. For comparison, I found a couple of other websites which use cover-wp: NELA Lives, Hedge Futures, and Marco Luthe. I’m not familiar with these websites, I just looked for websites that use the cover-wp theme. Eventually, any of these websites might disappear, or any of them might change themes, including Virginia CURE. But as I’m writing this they’re good examples. Virginia CURE’s excepts have the paragraphs preserved, the others don’t. The cover-wp theme uses a customized get_the_excerpt function, which in turn calls the built-in php function striptags to remove all of the tags.

Here’s copy of functions.php which I altered from cover-wp. I renamed it functions.txt so it can be viewed. I also made a small change to get_the_excerpt.

If anyone who reads this knows of a better function then I’d be interested. I’m just an amateur hack. But for now, my function works better than anything I found.

I fixed a couple of problems since I first posted it.

Combining feeds

A friend suggested I combine my schizophrenic group of blogs into a single blog. I’m not ready to do that but I did decide to display a feed from all my blogs on a single page. Easy enough, but I wanted to combine them all and sort them by date, rather than have a separate feed for each. I found a simple feed puller in a book called Plug-In PHP, by Robin Nixon. From that I modified and expanded the code to allow combining different feeds into one. Also, the books code worked for my wordpress blogs, but then I also wanted to make another feed which included blogs that I read, and my daughter users blogger. I had to write new code to accept her feed. I don’t know if this takes all kinds of feeds, but it does what I’ve needed it to do so far.

<?php
//Sort posts by publication date
function sortpdate ($a, $b) {
 $adate=strtotime($a[‘pubdate’]);
 $bdate=strtotime($b[‘pubdate’]);
 if ($adate == $bdate) { return 0;}
 return ($adate < $bdate) ? 1 : -1;
}

function RSStoHTML ($urllist, $usortfunction, $fullposts=3, $maxposts=12, $titletag=”h4″, $classprefix=””) {
//Takes blog posts from multiple blogs using a comma separated list of feeds, usorts them, and echos them
 

 $urls=explode(‘,’, $urllist);
 $i=0;

 $showitems=array();

 foreach ($urls AS $url) {
  
  $rss=file_get_contents($url);
  $xml = simplexml_load_string($rss);

  //The two foreach loops that follow pickup wordpress and blogger
  //I don’t know yet if these fail on other types of feeds.
  foreach($xml->channel->item as $item) {
   $i++;
   $showitems[$i][‘blogtitle’]=@$xml->channel->title;
   $showitems[$i][‘bloglink’]=@$xml->channel->link;
   $showitems[$i][‘blogdescription’]=@$xml->channel->description;
   $showitems[$i][‘itemlink’] = @$item->link;
   $showitems[$i][‘pubdate’] = @$item->pubDate;
   $showitems[$i][‘itemtitle’] = @$item->title;
   $showitems[$i][‘summary’] = @$item->description;
   $showitems[$i][‘content’] = @$item->children(“content”, true)->encoded;
  }
  foreach($xml->entry as $item) {
   $i++;
   $showitems[$i][‘blogtitle’]=@$xml->title;
   foreach (@$xml->children()->link AS $child ) {
    if($child->attributes()->rel==’alternate’) {
     $showitems[$i][‘bloglink’]=$child->attributes()->href;
    }
   }
   $showitems[$i][‘blogdescription’]=@$xml->subtitle;
   foreach (@$item->children()->link AS $child ) {
    if($child->attributes()->rel==’alternate’) {
     $showitems[$i][‘itemlink’]=$child->attributes()->href;
    }
   }
   $showitems[$i][‘pubdate’] = @$item->published;
   $showitems[$i][‘itemtitle’] = @$item->title;
   $showitems[$i][‘summary’] = substr ( @$item->content , 0, 30 );
   $showitems[$i][‘content’] = @$item->content;
  }
 }
 usort($showitems, $usortfunction);
 $out=””;
 $i=0;
 while ($i < sizeof($showitems) && $i < $maxposts) {
  if($i<$fullposts) {
   echo ‘<‘, $titletag, ‘ class=”‘, $classprefix, ‘itemtitle”><a href=”‘, $showitems[$i][‘itemlink’], ‘”>’, $showitems[$i][‘itemtitle’], ‘</a></’, $titletag, ‘><p class=”‘, $classprefix, ‘pubdate”>’, (date(‘l, Y F d’, strtotime($showitems[$i][‘pubdate’]))), ‘ in <a href=”‘, $showitems[$i][‘bloglink’], ‘”>’, $showitems[$i][‘blogtitle’]>”” ? $showitems[$i][‘blogtitle’] : $showitems[$i][‘bloglink’], ‘</a></p><p class=”‘, $classprefix, ‘summary”>’, $showitems[$i][‘content’], ‘</p>’;
  } else {
   echo ‘<‘, $titletag, ‘ class=”‘, $classprefix, ‘itemtitle”><a href=”‘, $showitems[$i][‘itemlink’], ‘”>’, $showitems[$i][‘itemtitle’], ‘</a></’, $titletag, ‘><p class=”‘, $classprefix, ‘pubdate”>’, (date(‘l, Y F d’, strtotime($showitems[$i][‘pubdate’]))), ‘ in <a href=”‘, $showitems[$i][‘bloglink’], ‘”>’, $showitems[$i][‘blogtitle’]>”” ? $showitems[$i][‘blogtitle’] : $showitems[$i][‘bloglink’], ‘</a></p><p class=”‘, $classprefix, ‘summary”>’, $showitems[$i][‘summary’], ‘</p>’;
  }
  $i++;
 }

 echo $out;
}

$urls=”http://bnmng.wordpress.com/feed,http://bnmngfarming.wordpress.com/feed,http://bnmngcomputing.wordpress.com/feed,http://healthyinsuffolk.wordpress.com,http://sashabyproxy.wordpress.com&#8221;;
RSStoHTML ($urls, ‘sortpdate’);

A variation of joomlart’s ja_purity template with resizable header and horizontal submenu

Update 2010 July 6: I’m still getting a few hits on this and I apologize to everyone who may have gotten headaches trying to make this work. This procedure provided a good solution for me at the time but there are much easier ways to achieve the dropdown menu, which can be found in the joomla documentation.

All below is from the original post.

I made a template which is basically a copy of ja_purity with a modified header and support for the submenu which I described in earlier posts. I’m still new at this so I’m sure there will be some problems and I want to hear about them so, please tell me.

Download bnmng_ja_purity_1_0.zip

In addition to standard ja_purity features:
Header images can be of a different size than the original images.
Entire header can be resized to match taller or shorter header images.
Mask can turned on or off on left and right side independently.
Logo text can be displayed with logo image.
Horizontal Submenu’s can be added by creating the menus. You don’t have to alter the index or css.

Notes:
I removed some of the choices for the template width.
You should know the height and width of your header and logo images

To install, put the bnmng_ja_purity folder in your templates directory, than put the language files (not the folders that they are in) in the appropriate folder. The name of the folder that the zipped language files are in tells you where they should go.

To create horizontal submenus, create a new Joomla menu to represent each submenu. Using module manager, set the position of each horizontal submenu to “subhornav” and set the menu assignment for each to display when the appropriate menu item from the parent horizontal menu is selected. Remember that the horizontal submenus are actually individual menus.

The ja_purity template on which I based my version was made by joomlart.

Dropdown menus in ja_purity

On my post about the submenu bar, I had a question about dropdown menus, but at the time I couldn’t answer.

I most cases, getting dropdown menus to work should be pretty easy, although there are a lot of posts on various forums from people having difficulty. The reason I was having trouble getting it to work earlier was I had modified my css files for other reasons, and accidentally wiped out important parameters.

The instructions for making the dropdown menus work in ja_purity are in various places, including a post on Learn Web Design Online.com . But if you read that post and are still lost, you may have to back up and learn how to make the sub-menus in the first place. Those instructions are at Joomla’s documentation site.

Having the dropdown menus along with the submenu bar means maintaining two separate sets of submenus. I don’t see any way to link them automatically. One set of submenus are the dropdowns, which you create by the instructions that I mentioned in the previous paragraph, and one set of submenus are the menus that appear in the sub-menu bar, which you create in accordance with my post (linked in the first paragraph). Any changes you make have to made in both.

Adding a Horizontal Submenu in Joomla’s JA Purity Template. [this post is outdated]

[Update: 2011 Sep 5: Very few people read this blog and yet I get a lot of hits on this post. It’s outdated, and wasn’t a good solution even when I wrote it. I’m only keeping it here as a record of things that I’ve written but please don’t waste any time following these instructions.]

[Update: 2010 Jun 6: Some of the folks hitting this post may be interested in my variation of ja_purity.]

The popular Joomla template JA Purity, which is included by default in new Joomla installations, has, theoretically, an easy-to-add suckerfish dropdown menu. But if you take a look at forums, you’ll see that a lot of people have trouble making it work.

Personally, I avoid dropdowns and other client side activity because I don’t trust that something that works fine on my laptop will work on other computers. I prefer letting the server do most of the work and trying real hard to limit the amount of work that has to be done. People developing in javascript or using cutting edge CSS really should have access to enough computers to test in Linux (or BSD), Windows, and Mac.

I also don’t like glitz, and I think dropdown menus are usually tacky.

For Virginia C.U.R.E.’s website, I added a bar under the horizontal menu.

[Update: 2010 Jun 6: At the request of the client, I added a drop-down menu to the website]

If you already have the horizontal menu working in JA Purity, and you understand enough php recognize a “block” of code, you should be able to add a submenubar below the horizontal menu as follows:

Open up index.php, which is in the ja-purity folder. There are two blocks of code which contain the word “hornav”. One block is around line 47 and the other is around line 145. My line numbers may be off because I’ve done some other messing around with my file. Copy each of these blocks and paste the copy right below. In the first copied block, replace “hornav” with “subhornav” and ja-sosdmenu.css with ja-sosdsubmenu.css. In the second copied block, replace “hornav” with “subhornav”, “mainnavwrap” with “subnavwrap”, and “mainnav” with “subnav”.

In your templateDetails.xml file, make a “subhornav” position line below the existing “hornav” position line.

Now, copy the file ja-sosdmenu.css to a new file called ja-sosdsubmenu.css. In ja-sosdsubmenu.css, look for all blocks with the word “sub” in them and delete them ( I don’t know if you have to do this, but you don’t need them and it might prevent some confusion later). Then do a global search and replace on the word “main” and replace it with the word “sub”. This is the css file that will control the way your submenu looks. In the block that starts with “#ja-subnav a {“, I changed “padding 10px 20px” to “padding 1px 20px”, and did the same in the block which starts with “#ja-subnav ul.menu li a {“. This made the submenubar thinner than the main menubar.

That’s all the coding that you need to do. The rest of the work is in the Joomla control panel.

Make a submenu for each menu item in your horizontal menu, even if that submenu only has one, blank menu item. You can use &nbsp; for the title of the blank item, and the link can be to your home page. If you don’t have something there then the menu bar won’t display, and it doesn’t look good for the submenubar to appear and disappear as users click around.

For example, for the menu item About Virginia C.U.R.E. in the main horizontal menu, there is another menu called About Submenu, which contains items for Organizational Information, Chapters, and Board Members. For the Announcements, there is an Announcements submenu, which only contains one choice; the blank one.

For each new submenu, make a copy of the Main Menu module and associate the copy with the submenu. Edit the new module, and set the position of the module to “subhornav”. [Update (2010 April 11): travitzki linked to this post on a joomla forum but noted that you don’t need a new module for each submenu. ]

In the Menu Assignment section, choose the option labeled “Select Menu Item(s) from the List”, and select the appropriate menu items for which you want the particular submenu to appear. That will include the parent item from the main horizontal menu, and the siblings from the submenu. You have to keep up on this when you add new menu items.

That should do it. I only tested this on Virginia C.U.R.E.’s website, and I’ve done some other changes prior to this, so there may be some inconsistency between how this works on Virginia C.U.R.E.’s website and how it works on a new installation of JA Purity. If you comment about any trouble with this, I’ll try to help.