джангонавт.ru

Archive for the ‘Основная лента’ Category

create_object и дополнительные поля создаваемого объекта

with 2 comments

Среди убойных возможностей django хочется отметить generic views. Поначалу я их не заметил и практически никак не использовал, но после прочтения этой статьи присмотрелся внимательней и проникся. Сейчас иногда получается, что они напрямую или через функции-обертки реализуют почти весь view-слой сайта.

Одной из проблем, с которой я столкнулся при использовании generic views — это вопрос о том, как добавить в объект, создаваемый через create_object, дополнительные данные. Например, при создании новой статьи хочется автоматом прописывать залогиненного пользователя, который её написал. Поскольку create_object создает и использует ModelForm внутри себя, то способ передачи request.user в эту форму не совсем очевиден.

А способ-то довольно прост — достаточно описать ModelForm внутри функции-обертки над create_object. Если в этом описании переопределить метод save(), то request будет в пределах видимости.

Вот наша модель (models.py):

from django.contrib.auth.models import User
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    author = models.ForeignKey(User)

Вот форма (forms.py):

from django import forms
from articles.models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ('title', 'content', )

А вот та самая функция обертка:

from django.views.generic.create_update import create_object
from articles.forms import ArticleForm

def create_article(request):
    class AddArticleForm(ArticleForm):
        def save(self):
            article = super(AddArticleForm, self).save(commit=False)
            article.author = request.user
            article.save()
            return article
    return create_object(request, form_class=AddArticleForm, login_required=True)

Written by finn

Август 30th, 2009 at 12:49 дп

direct_to_template вместо render_to_response

with 6 comments

Везде и всюду для загрузки и рендеринга шаблона для последующей отдачи браузеру рекомендуется использовать функцию render_to_response. Однако, если её использовать правильно, а не так, как написано в туториале, то помимо самой функции приходится импортировать еще и RequestContext, и явно передавать его:

from django.shortcuts import render_to_response
from django.template import RequestContext

def my_view(request):
    return render_to_response('my_template.html',
                              {'object_list': SomeModel.objects.all()},
                              context_instance=RequestContext(request))

То есть по факту получается, что это не такой уж и shortcut. Такой многословный код приводит к тому, что многие программисты делают собственные версии render_to_response, которые одним из параметров принимают request. А особо продвинутые джангонавты даже мудрят с декораторами.

Однако в django уже есть функция, которая фактически делает то же самое. Смотрите сами:

from django.views.generic.simple import direct_to_template

def my_view(request):
    return direct_to_template(request, 'my_template.html',
                              {'object_list': SomeModel.objects.all()})

Все generic views правильно работают с RequestContext и direct_to_template тут не исключение.

Written by finn

Август 23rd, 2009 at 12:01 дп

Admin actions в качестве views

with one comment

Долгое время в джанговской админке не было встроенной поддержки для массовых действий над записями. К примеру как только не приходилось извращаться для хотя бы чуть более удобного удаления объектов.

В версии 1.1 джангонавтам привалило счастье - групповые операции. Удаление кучки объектов теперь есть прямо из коробки. При этом, как обычно это бывает в django, использовать новый механизм легко и приятно - написание простенького action укладывается в две-три строчки. Больше того, action может не только как-то молча обрабатывать группу объектов, но и возвращать HttpResponse, в котором пользователь может проделать дополнительные действия.

Документация в качестве примера предлагает возвращать HttpResponseRedirect и передавать список обрабатываемых объектов в виде GET-параметра полноценной view для последующей самостоятельной работы. Однако, ничего не мешает использовать наш action в качестве view-функции.

Допустим у нас есть магазин, в котором товары разбиты по категориям:

class Category(models.Model):
    name = models.CharField(u'Наименование', max_length=100)

    def __unicode__(self):
        return self.name

class Product(models.Model):
    category = models.ForeignKey(Category, verbose_name=u'Категория')
    name = models.CharField(u'Наименование', max_length=100)
    price = models.DecimalField(u'Цена', max_digits=15, decimal_places=2)

Время от времени пользователю хочется перенести пачку товаров из одной категории в другую (к примеру разбили «Молоко» на «Молоко настоящее»/«Молочные продукты» и надо раскидать товары согласно указаниям санэпидстанции и угрызениям своей совести). Чтобы человек не мучался, выставляя каждому такому товару новую категорию, мы и напишем нехитрый action.

class CategoryForm(forms.Form):
    category = forms.ModelChoiceField(queryset=Category.objects.all())

class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'price', 'category',)
    actions = ['set_category_action']

    def set_category_action(self, request, queryset):
        if 'do_action' in request.POST:
            form = CategoryForm(request.POST)
            if form.is_valid():
                queryset.update(category=form.cleaned_data['category'])
                # Ничего не возвращаем, это вернет нас на список товаров
                return
        else:
            form = CategoryForm()
        return render_to_response(
            'admin/shop/set_category.html',
            {'title': u'Укажите категорию, в которую надо переместить товары',
             'objects': queryset,
             'form': form},
            context_instance=RequestContext(request))
    set_category_action.short_description = u'Переместить в категорию'

Поддерживающий это дело set_category.html прост. Через hidden-поля action и _selected_action мы заставляем админку опять вызвать наш action, но на этот раз в POST попадают две дополнительные переменные: do_action (флажок, что мы пришли из формы) и category (та категория, которую надо назначить).

{% extends "admin/base_site.html" %}

{% block content %}

    <form action="" method="post">

        <input type="hidden" name="action" value="set_category_action">
        <input type="hidden" name="do_action" value="yes">

        <div>
            {{ form.category }}
            <input type="submit" class="default" style="float: none" value="Переместить">
            {{ form.category.errors }}
        </div>

        <h2>Товары для перемещения</h2>

        <ul>
            {% for object in objects %}
                <li>
                    <a href="{{ object.pk }}/">{{ object.name }}</a> - {{ object.category }}
                    <input type="hidden" name="_selected_action" value="{{ object.pk }}">
                </li>
            {% endfor %}
        </ul>

    </form>

{% endblock %}

Admin action view

Вот таким простым образом мы избавились от необходимости писать лишний view, самостоятельно выдергивать в нем список объектов, придумывать url и делать редирект на change list.

Written by finn

Май 3rd, 2009 at 11:16 пп

Posted in Основная лента

Tagged with ,

Использование sorl.thumbnail без template tags

without comments

Практически в каждом django-проекте рано или поздно встает вопрос о генерировании превьюшек для картинок.  Одним из наиболее простых и удобных решений для этого является sorl.thumbnail.  Это приложение позволяет генерировать превьюшки либо прямо в шаблоне через тег {% thumbnail %}, либо используя в моделях специальное поле ImageWithThumbnailsField.  В подавляющем большинстве случаев этого достаточно, но иногда возникает необходимость получить превьюшку в python-коде прямо по месту, без переделывания моделей.

К примеру может захотеться видеть совсем маленькие, нигде больше не используемые превьюшки в списке объектов в админке. Делается это на удивление просто.

class PatternAdmin(admin.ModelAdmin):
    list_display = ('icon', '__unicode__',)
    list_display_links = ('__unicode__',)

    def icon(self, obj):
        from sorl.thumbnail.main import DjangoThumbnail
        thumbnail = DjangoThumbnail(obj.photo, (60, 60))
        return u'<img src="%s" border="0" alt="" width="%s" height="%s" />' % \
                            (thumbnail.absolute_url,
                             thumbnail.width(), thumbnail.height())
    icon.short_description = u'Фото'
    icon.allow_tags = True

В результате получаем вот такой список объектов:

sorl.thumbnail в django admin

Written by finn

Май 2nd, 2009 at 10:23 пп