Показване на сложни речници в Django Templates

Използвам Django 1.4 с Python 2.7 на Ubuntu 12.04.

Имам шаблон, който искам да попълня с информация относно разработчиците, работещи по проект.

Всеки разработчик ще има следната информация за показване:

type
title
first_name
last_name
skills

Проблемът, с който се сблъсквам, е, че всеки разработчик има много умения, свързани с него.

Създадох модела така:

class DevSkills(models.Model):
    dev = models.ForeignKey(User)

    skill = models.CharField(max_length = 200)

Опитвам се да създам изглед, който ще попълни речника, така че да мога да преглеждам всеки разработчик, да показвам тяхната информация, след което да преглеждам всяко умение (за да ги покажа един по един).

Ето гледката:

def developers(request):
    """
    ..  function:: developers()

        Provide the page that shows the developer credentials

        :param request: Django Request object
    """
    devs = User.objects.filter(is_staff = True)
    dev_info = {}
    for dev in devs:
        dev_info.update(createDevDisplayDict(dev))
    data = { 'user' : request.user }
    data.update({ 'devs' : dev_info })

    return render_to_response("developers.html", data)

Означих полето is_staff от User, за да покажа, че потребителят е разработчик.

Създадох проста помощна програма, която ми помага да попълня вградените речници, за да мога да ги преглеждам:

def createDevDisplayDict(user):
    """
    ..  function:: createDevDisplayDict()

        Create a dictionary for showcasing the developer

        :param user: developer who we are working with
    """

    userProfile = UserProfile.objects.get(user = user)
    devSkills = DevSkills.objects.filter(dev = user) 
    dev_dict = {}
    user_dict = { 'dev_type'        : userProfile.dev_type,
                  'title'           : userProfile.title,
                  'first_name'      : user.first_name,
                  'last_name'       : user.last_name,
                }
    dev_dict.update(user_dict)

    skill_dict = {}
    for skill in devSkills:
        skill_dict.upate({ 'skill' : skill.skill })

    dev_dict.update(skill_dict)
    return dev_dict

Моето намерение е да прегледам всеки разработчик, да създам "супер" речник, който да съдържа всеки от техните user_dict речници (които са базирани на тяхната User информация) и да добавя към това речник за всяко от техните умения. След това, обратно в шаблона, искам да превъртя "супер" речника по такъв начин, че да им представи нещо като следното:

James Taylor
Project Lead
Software Developer
    • Django
    • Python
    • JavaScript
    • JQuery

Elizabeth Norton
Design Lead
Graphic Designer
    • Edge
    • Adobe Photoshop
    • Adobe Illustrator
    • CSS

Ето шаблона, с който се опитвам да работя:

{% extends "base.html" %}

{% block content %}
<div>
    <p>Our Developers</p>
</div>
    {% for dev in devs %}
        {{ dev.user_dict.first_name }} {{ dev.user_dict.last_name }}
        {{ dev.user_dict.title }}
        {{ dev.user_dict.dev_type }}

        <ul> 
        {% for skill in dev.skill_dict %}
            <li>skill.skill</li>
        {% endfor %}
        </ul>
    {% endfor %}
{% endblock %}

Когато виждам страницата сега, изглежда така:

Our Developers

Да...нищо не се запълва. Някакви предположения?

АКТУАЛИЗАЦИЯ 1: Промених моята помощна програма по предложение на iMom0. Сега използвам списък за всяко умение. Така:

def createDevDisplayDict(user):
    """
    ..  function:: createDevDisplayDict()

        Create a dictionary for showcasing the developer

        :param user: developer who we are working with
    """

    userProfile = UserProfile.objects.get(user = user)
    devSkills = DevSkills.objects.filter(dev = user) 
    dev_dict = {}
    user_dict = { 'dev_type'        : userProfile.dev_type,
                  'title'           : userProfile.title,
                  'first_name'      : user.first_name,
                  'last_name'       : user.last_name,
                }
    dev_dict.update(user_dict)

    skills = []
    for skill in devSkills:
        skills.append(skill.skill)

    skill_dict = {'skill' : skills}

    dev_dict.update(skill_dict)
    return dev_dict

Виждам ползата от това - всъщност е много по-интуитивно и мисля, че го направих твърде трудно по друг начин. Но моят шаблон все още се показва гол. :(

АКТУАЛИЗАЦИЯ 2:

Знам, че сега съм на път да пиша. Сложих малко регистриране в изгледа:

devs = User.objects.filter(is_staff = True, is_superuser = False)
dev_info = {}
for dev in devs:
    dev_info.update(createDevDisplayDict(dev))

for key in dev_info:
    for sub_key in dev_info[key]:
        logfile.write('{0} = {1}\n'.format(sub_key, dev_info[key][sub_key]))

И регистрационният файл показва:

skills = [u'Java', u'Perl', u'C++', u'Python', u'Django']
dev_type = Software Developer
first_name = Rico
last_name = Cordova
title = Owner

Така че трябва да е начинът, по който го наричам в шаблона, нали?

АКТУАЛИЗАЦИЯ 3:

Разбрах, че изключвам user_dict и техния skills. Така че промених леко помощната програма, за да ги събера в един речник.

## Create a logging object
userProfile = UserProfile.objects.get(user = user)
devSkills = DevSkills.objects.filter(dev = user) 
dev_dict = {}
user_dict = { 'dev_type'        : userProfile.dev_type,
              'title'           : userProfile.title,
              'first_name'      : user.first_name,
              'last_name'       : user.last_name,
            }

skills = []
for skill in devSkills:
    skills.append(skill.skill)

user_dict.update({ 'skills' : skills })

dev_dict['user_dict'] = user_dict
return dev_dict

Това е много по-добро решение според мен. Въпреки това все още имам проблеми с достъпа до информацията user_dict в шаблона. :(


person Rico    schedule 20.10.2012    source източник


Отговори (3)


Може да използвате ORM функциите на Django, за да направите това много по-лесно (и, ще видим, ще получите по-добра производителност), това е страхотна функция!

Код на модела

class DevSkill(models.Model):
    dev     = models.ForeignKey(UserProfile, related_name = 'skill_set')
    skill   = models.CharField(max_length = 200)

Променихме две неща:

  • Използването на UserProfile ForeignKey вместо user ще опрости останалата част от кода. Тъй като така или иначе имате UserProfile ‹-> User картографиране, това няма да е проблем.
  • Добавихме атрибут related_name, така че всеки UserProfile обект вече има атрибут skill_set, който съхранява неговия списък от DevSkills.

(Моля, имайте предвид, че related_name не се изисква и Django ще създаде общ атрибут modelname_set, ако не го зададете). Освен това DevSkill трябва да е единствено число, обектът е едно умение!

Очаквам също, че имате следното за UserProfile и сте създали код, ако приемем, че сте го направили. Ще трябва да се адаптирате, ако не го направите.

class UserProfile(models.Model):
    user     = models.OneToOneField(User)
    title    = models.CharField(max_length = 40)
    dev_type = # W/E you want

В изгледа:

devs = UserProfile.objects.all() # Or W/E queryset would fit.
# Pass context & all.

В шаблона:

{% extends "base.html" %}

{% block content %}
<div>
    <p>Our Developers</p>
</div>
    {% for dev in devs %}
        {{ dev.user.first_name }} {{ dev.user.last_name }}
        {{ dev.title }}
        {{ dev.dev_type }}

        <ul> 
        {% for skill in dev.skill_set.all %}
            <li>skill.skill</li>
        {% endfor %}
        </ul>
    {% endfor %}
{% endblock %}

производителност

Моля, имайте предвид, че този код (този, който използвате и сега) ще убие абсолютно производителността. Наистина, ние правим няколко заявки за всеки потребител (попадане на базата данни за техния потребител и техните DevSkills).

Това обаче не е проблем, можем да използваме ORM на select_related и prefetch_related функции за решаване на този проблем:

devs = UserProfile.objects.select_related('user').prefetch_related('skill_set').all()
По този начин правим само две заявки, една за UserProfile -> Userи една за DevSkill, за които обединяването се извършва в Python, но това не трябва да ви интересува, Django го прави вместо вас.

Моля, имайте предвид, че prefetch_related е функция на Django 1.4.


Бележка под линия: нещата UserProfile изчезват в Django 1.5, вижте го!

person Thomas Orozco    schedule 20.10.2012
comment
Съжалявам, но това не работи за мен. Ако използвам заявката devs = UserProfile.objects.all(is_staff = True, is_superuser = False), получавам грешка, защото обектът User има атрибутите is_staff и is_superuser, а не моделът UserProfile. Как мога да филтрирам UserProfile по потребителите, които са is_staff, а не is_superuser? - person Rico; 20.10.2012
comment
Добре, успях да използвам последната заявка, която поставихте devs = UserProfile.objects.select_related('user').prefetch_related('skill_set').all(), но тя връща всички потребители. Искам само is_staff, а не is_superuser. Как мога да направя това? - person Rico; 20.10.2012
comment
Разбрах! Току-що го обработих в шаблона с обикновен {% if dev.user.is_staff and not dev.user.is_superuser %}. Много благодаря! - person Rico; 20.10.2012
comment
@Rico Това ще работи, но вие все още питате за всичко и само решавате дали да го покажете или не. Ако имате голям брой потребители, това може да се превърне в проблем. Трябва да използвате UserProfile.objects.filter(user__is_staff = True, user__is_superuser = False).... Синтаксисът rel__field ви позволява да следвате връзките в заявките. - person Thomas Orozco; 21.10.2012

dict.update винаги презаписва стойността на dict

In [2]: d = {'key': 'value'}

In [3]: d.update({'key': 'value1'})

In [4]: d
Out[4]: {'key': 'value1'}

Вместо това трябва да използвате list и list.append.

И вашият шаблон не знае какво е user_dict, коригирайте го,

dev_dict['user_dict'] = user_dict
person iMom0    schedule 20.10.2012
comment
@Rico d['key'] = стойността е същата като d.update({'key': value}) не d.update(value) - person iMom0; 20.10.2012

Това е нова концепция за мен - dict.items в шаблон. Следното е точно как успях да покажа това, което исках.

{% extends "base.html" %}

{% block content %}
<div>
    <p>Our Developers</p>
</div>
    {% for key, value in devs.items %}
        {{ value.first_name }} {{ value.last_name }} <br>
        {{ value.title }} <br>
        {{ value.dev_type }} <br>
        <ul> 
        {% for skill in value.skills %}
            <li>{{ skill }}</li>
        {% endfor %}
        </ul>
    {% endfor %}
{% endblock %}
person Rico    schedule 20.10.2012
comment
Можете да използвате dict.values(), ако не се нуждаете от key. Но ако не се нуждаете от ключа, вероятно трябва да използвате list вместо него. - person Thomas Orozco; 20.10.2012