Post on Request

Published at January 30, 2011 | Tagged with: , , , ,

In the last few weeks I have been little busy(and suffering from lack of inspiration) so my blog lacks of new posts but I see that there are people visiting it(especially for the Django CMS stuff) so I am willing to start a "Post on Request" practice.

The idea is simple - I will collect post theme requests in the first three weeks(or 21 days) of every month and after a choose based on your votes( and my decision ) I`ll go out with a post on the selected theme by the end of the week. Please keep your question in the following areas - Django, Django CMS, Web Development. Question in other areas will be considered too, but may stand out of my knowledge area. You can place your request here or check the list and vote for another one or just comment this post.
I hope that you will find this initiative interesting and join it.

Django forms ChoiceField and custom HTML output...

Published at January 6, 2011 | Tagged with: , , , ,

... or what to do in case that you need a special design for your choice fields

The problem: Few posts ago I talked(wrote) about Django forms ChoiceField and with dynamic values and now it is time to take a look at the front end and how we display these values to the user. I will use the code from that post:


class MyForm(forms.Form):
    my_choice_field = forms.ChoiceField(choices=MY_CHOICES)

By default Django provide us with a simple drop-down list as visual representation of the choice field. Just create and instance of the form in your view, pass it in the context:


def my_view(request):
    form = MyForm()
    return render_response('template.html',{'form': form})

and then display it in the template using {{form.my_choice_field}}. Speciality: For most cases this drop-down works just fine but sometimes this is not enough. Lets say that instead of drop-down list we need a radio buttons. There are two ways to achieve this - using a custom widget or playing with the template tags and the ChoiceField object. The first solutions is more suitable if you have a repeatable interface for several fields. In my case this was field specific so I chose the second one as faster for implementation. Solution: You can access the field object and its choices calling the field respectively field.choices properties of the form element in the template. Here is an example


<ul>
{% for choice in form.my_choice_field.field.choices %}
  <li>
    <input type="radio" name="my_choice_field" value="{{choice.0}}"
      {% ifequal form.my_choice_field.data choice.0 %} 
         checked="checked"
      {% endifequal %}/>
    <label for="">{{choice.1}}</label>
  </li>
{% endfor %}
</ul>

This allows you to specify custom styles/HTML for every element and also to distinct first and last elements using forloop.first and forloop.last - for more information you can check Django documentation about the for template tag

Final words: I did not have the chance to measure the speed difference between these two methods I mentions but I intend to post a also an example of the first one(using custom widget) and a "benchmark" between them both. If someone has already done this please post the results and your opinion about this.

Extending Django CMS Page model – part III

Published at January 4, 2011 | Tagged with: , , ,

... or the drawback of Extending Django CMS Page model – part II

First of all please accept my apologies that I couldn't warn you for this drawback earlier but I was really busy in the last few weeks so let's skip directly to the problem.
If you remember in Extending Django CMS Page model – part II we added a method to the NavigationNode class that return an instance of the corresponding Page object. If we use it in the way show here:


from menus.base import NavigationNode
NavigationNode.page_instance = lambda u: Page.objects.filter(pk = u.id)[0]  

The lambda function is executed every time when page_instance is called so this definetly increases the number of SQL queries made(+1 for every call of page_instance). Although this query is done using primary key and it is really fast this can bring you troubles. Especially if your hosting provider give you a limit of queries per time.
The good point is that it is not so big problem if you do it once or twice per page and it can be really useful. It all depends from how you use it. Also if you cache the output HTML you will skip the database overhead(but you have to be careful and make a mechanism that will invalidate the cache after page modifications)

(Almost) everything has its strong and weak sides, but using it carefully may bring you what you want. I really hope that someone will find this useful. If you do or you have found another weak/strong side do not hesitate to share your thoughts.

Extending Django CMS Page model - part II

Published at December 14, 2010 | Tagged with: , , , ,

... or how to use the extended model in your templates and especially in the built-in navigation

The problem: As you can see in the previous post there is a very simple way to extend the page model with some custom fields without changing the core code. But how to use your custom fields inside your templates? If you have an instance of the Page model everything is fine. You can just access its properties and everything is fine. Unfortunately there is a problem if you try it inside a custom template used for show_menu template tag.

Speciality: I was really surprised when I tried to access my custom properties inside the mentioned above template and nothing happened. After some investigation I found that the elements in show_menu are not instances of the Page model but of NavigationNode. And even more surprised that NavigationNode does not contain instance of the Page model in its properties. So I was in need to extend the NavigationNode class.

Solution: First of all I decided to make it easier to access the custom properties I created before, so I added the following code to it:


class PageAvatars(models.Model):
    page = models.ForeignKey(Page, unique=True, verbose_name=_("Page"),
        editable=False, related_name='extended_fields')
    big_avatar      = FileBrowseField(_(u'Big Avatar'), max_length=255, blank=True)
    small_avatar    = FileBrowseField(_(u'Small Avatar'), max_length=255, blank=True)
    
def _big_avatar(obj):
    for e_f in obj.extended_fields.all():
      return e_f.big_avatar
    return None
Page._big_avatar = _big_avatar     
Page.big_avatar = property(lambda u: u._big_avatar())

For simplicity I have skipped the imports, but you can check them in the previous post So now I have a "shortcut" for accessing the big_avatar and it is time to make the Page object available in the NavigationNode.


from menus.base import NavigationNode
NavigationNode.page_instance = lambda u: Page.objects.filter(pk = u.id)[0]  

Line 2 defines a property("page_instance") that returns a Page object based on the primary key of the NavigationNode.id that equals to the corresponding Page primary key. Now you can use it in your custom navigation templates in the following way.


{% for child in children %}
    {{child.page_instance.big_avatar}}
{% endfor %}

Final words: I need to confess that I am really, really amazed how simply things can be done in Django and Django CMS. It is really awesome. I hope that you will find this post really helpful and useful. And as always do not be afraid to propose a better solutions or to ask for deeper explanation if something is not clear enough.

Extending Django CMS Page Model

Published at December 12, 2010 | Tagged with: , , , , ,

... or how to add some fields to the Page model in the admin without changing Django CMS core

The problem: Some times the Page model just lack of something you need. In my case this was "page avatars". Long story short - I needed an image/avatar for each page in my CMS. So what I have to do was to add a field to the Page model that will keep the info about the avatar path.

Speciality: Of course the simplest solution was just to edit the file(pagemodel.py) that contain the Page model. Unfortunately in the common case(and in my too) several different application resides on a single server and share the same Django CMS. So changing the core code is inconvenient because every change on it will affect all applications that rely on the same CMS core. So what? Copying the whole Django CMS files in my app and modifying them? This does not seems like a good solution too. Fortunately there is a simple way to do that and leave the core code unchanged.

Solution: First you need to create a new model that will hold the new fields you need and a foreign key to the Page model.


from django.db import models
from django.utils.translation import ugettext_lazy as _
from cms.models.pagemodel import Page
from filebrowser.fields import FileBrowseField as FBF

class PageAvatars(models.Model):
    page = models.ForeignKey(Page, unique=True, verbose_name=_("Page"),
        editable=False, related_name='extended_fields')
    big_avatar      = FBF(_(u'Big Avatar'), max_length=255, blank=True)
    small_avatar    = FBF(_(u'Small Avatar'), max_length=255, blank=True)

After you define the model it is time to make the admin.


from extended_pages.models import PageAvatars
from cms.admin.pageadmin import PageAdmin
from cms.models.pagemodel import Page
from django.contrib import admin

class PageAvatarsAdmin(admin.TabularInline):
    model = PageAvatars
    
PageAdmin.inlines.append(PageAvatarsAdmin)

admin.site.unregister(Page)
admin.site.register(Page, PageAdmin)


The last two lines(11 and 12) are used to force Django to reload the Page model admin.
Now you have your new field as an inline in the Page admin.

Final words: As you can see the solution is simple, fast and the core code is unaffected. There is an unique constraint on the page field in PageAvatars model so you will not be able to add more than one avatar per page.
If there is something unclear in the code or you have a better idea I am ready to hear it.