views:

634

answers:

3

Hi guys, i want to know if prepopulated_field is available outside of the ModelAdmin?

I want to make a slugfield where the value comes from the field title.

Regards, Asinox

+2  A: 

Not that I know of. I did something like this using javascript and AJAX: when the title field loses focus, an AJAX request with it is sent to a url in you app that returns a slug. Here's the javascript:

<script type="text/javascript">
Event.observe(window, 'load', function() {
    form = $("post_form");
    title = $('id_title');
    slug = $('id_slug');
    title.observe('blur', getSlug);
});

function getSlug() {
    var titleText = $F(title);
    ajax = new Ajax.Request("/blog/utils/get-slug", {
        'method': 'get',
        'parameters': {'title': titleText},
        'onSuccess': setSlug
    })

}
function setSlug(transport) {
    var slugText = transport.responseText;
    slug.value = slugText
}
</script>

It uses Prototype but you could just as easily use jQuery or whatever. On the Django side, here it the view that the AJAX request is directed to plus some helper functions:

def GetSlug(request):
    """This Django view provides a url-friendly slug based on a given post title. 
    It helps avoid primary key violations by creating different slug names 
    for posts with the same title.It is designed to be called by an AJAX 
    request from within the admin interface."""

    title = request.GET.get('title', None)
    title = unquote(title)
    if not title:
        return HttpResponseNotFound("")
    result = findSlug(title)
    if not result:
        result = ""
    return HttpResponse(result)    

def findSlug(title):
        title = urlify(title)
        words = title.split('-')
        if len(words) > 5:
            title = "-".join(words[0:5])
            extraWords = words[5:]
        else: 
            extraWords = []

        def testSlug(slug):
            try:
                Post.objects.get(slug=slug)
            except Post.DoesNotExist:
                return slug
            return False
        #if slug does not already exist, return that
        if testSlug(title):
            return title
        #if title had extra words, add them one at a time and test that
        else:
            for word in extraWords:
                title = title + "-" + word
                if testSlug(title):
                    return title
        #if we've run out of words and slug is _still_ not unique, try incrementing: title-2, title-3, etc. 
        incrementor = 2
        while incrementor < 10:
            tempTitle = title + "-%d" + incrementor
            if testSlug(tempTitle):
                return tempTitle
        #if all else fails, punt to the user
        return None

def urlify(string):
    """This takes a string (say, a post title) and convets it to something url-friendly

    >>> title = "I read \"War & Peace\" to-day"
    >>> urlify(title)
    i-read-war-and-peace-to-day

    """
    string = string.lower().strip() #remove leading, trailing whitespace
    string = string.replace("&", "and")
    string = re.sub("[^a-z0-9- ]", "", string) #remove non-alphanumeric chars  (except dashes and spaces)
    string = re.sub("\W+", "-", string) #replace whitespace with "-"
    string = re.sub("-{2,}", "-", string) #remove double dahses
    return string

The GetSlug view tries really hard to return something useful but unique, even in cases where the title might already exist for another record.

mazelife
+2  A: 

It's not immediately available, but you can make your own system quite easily my overriding the save() method in your model. Eg.

def save(self, *args, **kwargs):
    # If there is not already a slug in place...
    if not self.[name-of-slug-field]:
        # Import django's builtin slug function
        from django.template.defaultfilters import slugify
        # Call this slug function on the field you want the slug to be made of
        self.[name-of-slug-field] = slugify(self.[field-to-slugify])
    # Call the rest of the old save() method
    super([model-name], self).save(*args, **kwargs)

You'd need to change the things in square brackets to make them relevant to your model.

[name-of-slug-field] is the field in your model which holds the actual slug (eg. 'this-is-slugged')

[field-to-slugify] is the field you want to build the slug from (in your case, it will probably be title)

[model-name] is the name of the model that this method is in

The last line of that code is the only potentially complicated bit I think, it just adds the remainder of the old save() method in its place so when you call save() it still physically commits the changes to the database.

Obviously these changes will only be made server-side, so the user won't 'see' them happening, so if you want to emulate how the Django admin panel works exactly (ie. the slug automatically appears in the textbox for slug), you would need to incorporate mazelife's Ajax code.

A: 

Hi guy, thanks for your help, now ill test both excellent's replay :), ill let u know, im new with Django, but i think that ill use it for ever :)

@Martin v. Löwis: thansk for edit :)

Asinox