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.

Django forms ChoiceField with dynamic values...

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

... or how to get a dynamic drop-down list with Django forms

The problem: Lets say that you need a form with a drop-down list that have dynamic values. With Django this can be done simple and fast, but if you are new you may get yourself into a trap. In a standard form(with static values in the drop-down) your code will be something like this:


MY_CHOICES = (
    ('1', 'Option 1'),
    ('2', 'Option 2'),
    ('3', 'Option 3'),
)
    
class MyForm(forms.Form):
    my_choice_field = forms.ChoiceField(choices=MY_CHOICES)

So if you want the values to be dynamic(or dependent of some logic) you can simply modify your code to something like this:


def get_my_choices():
    # you place some logic here
    return choices_list

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

and here you will fail(not absolutely). This is a common mistake and sooner or later you will see what you messed but it my be too late. Speciality: the trick is that in this case my_choice_field choices are initialized on server (re)start. Or in other words once you run the server the choices are loaded(calculated) and they will not change until next (re)start. This mean that your logic will be executed only once. This will be OK if your logic is server specific(depends from server settings) or "semi-dynamic" in any other way. But you need this list to be updated on every form load you will need to use something the form __init__ method. Solution: fortunately the form`s class has an __init__ method that is called on every form load. Most of the times you skipped it in the form definition but now you will have to use it.


def get_my_choices():
    # you place some logic here
    return choices_list

class MyForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['my_choice_field'] = forms.ChoiceField(
            choices=get_my_choices() )


You first call the __init__ method of the parent class(Form) using the super keyword and then declare your dynamic fields(in this case my_choice_field). With this code get_my_choices is called on every form load and you will get your dynamic drop-down.

Final words: have in mind that this method also have one strong drawback. If your application have a heavy load, executing a logic on every form load may(will) bring you troubles. So some caching of the form or the logic result may be useful.
And remember If you have other approach, idea or question I am always ready to hear it.

SEOmoz meetup in Sofia, Bulgaria

Published at November 2, 2010 | Tagged with: , ,

First of all I want to thank to Rand Fishkin from SEOmoz and the Webit team for organizing this meetup.

For a long time in the web world there was a feud between the developers and the SEO guys. Both groups claiming that their work is more important, more sophisticated and with bigger value to the client and website visitors. A have to admit that(as a developer) I was also focused only on my work ignoring the SEO part and leaving it to the others. But for the last year I started to give more attention to this field.

Do not get me wrong, I am not going to become a SEO Expert but the truth is that like every other thing the web is complex system. For example - you can hire the best auto engineer but if he is alone you will never get a car that sells. You need a team - engineers, designer, managers, ergonomics experts etc. Everyone of them should focus in his field but also to cooperate with others to provide the optimal solutions.

As a developers our task is to know the basic principles of the semantic web and search engine optimization and to build applications conformable with them. Then the SEO experts come and do their magic. But it is a process that have to be walked hand by hand for both of us. It is always easier if it is made by design(architecture) the to rewrite it later.

As a final word I want to state that I have a really great time at this meeting and I hope that this was my first but not last attendance on event of this kind.