Архивы: python

Расширение профиля пользователя django

в Django я по прежнему новичок, но что-то стараюсь понять и запомнить.

стояла задача расширить профиль пользователя дополнительными полями, статей на эту тему пресс и малелькая тележка, но не получалось, то у кого-то на сайте парсер html сильно злой и код надо было переписывать, у кого-то были ошибки в примерах и т.п.

но как-то оно получилось, попробую собрать все мысли в кучу в эту статью.

Многие знают о django-registration и вот от автора этого замечательного приложения существует django-profiles котором я и воспользовался.

устанавливаем django-profiles

easy_install django-profiles

в models.py создадим модель профиля, которая наследуется от штатной User и добавим в ней нужное нам поле

from django.contrib.auth.models import User
...
...
class UserProfile(models.Model):
    # поле для связки со встроенной моделью пользователя Django
    user = models.ForeignKey(User, unique=True)
    # наше новое поле
    about = models.TextField(blank=True)

Далее в settings.py надо указать кто будет новым AUTH_PROFILE_MODULE

AUTH_PROFILE_MODULE = 'list.UserProfile'

где list это ваше приложение в котором расположена модель расширяющая пользователя

в принципе на этом можно закончить, ибо обращаться теперь можно к

{{ user.get_profile.about }}

только нужно обратить внимание что надо пользоваться не context а RequestContext
и указать это в
TEMPLATE_CONTEXT_PROCESSORS файла settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
     ...
     'django.contrib.auth.context_processors.auth',
     ...
     )

но мы идём дальше

Теперь нам надо подключить django-profiles для просмотра\редактирования профиля

в файле urls.py
подключаем

    ...
    (r'^profiles/', include('profiles.urls')),
    ...

в settings.py также подключим приложение

INSTALLED_APPS = (
     ...
     'profiles',
     )

теперь создадим таблицы в базе

python manage.py syncdb

приложение profiles использует вот такие шаблоны

    profiles/create_profile.html
    profiles/edit_profile.html
    profiles/profile_detail.html
    profiles/profile_list.html

по названию понятно что к чему

для начала отредактируем edit_profile.html

{% extends "base.html" %}
{% block title %}Edit Profile{% endblock %}
 
{% block content %}
<h1>Edit contact info for {{ user }} </h1>
 
<form method="POST" action="">
    {{ form }}
    <input type="submit" name="submit" value="Update" id="submit">
</form>
 
{% endblock content %}

сохраняем, теперь можно обратиться к /profiles/edit/ то должно открыться форма редактирования профиля.

бывают ситуации, что в профиле есть какие-то поля, что запрещено редактировать пользователю, реализуется это довольно просто

создаём класс формы, наследуемой от общей формы, в котором перечисляем, то что нам исключить от вывода пользователю
forms.py

from django.db import models
from django.forms import ModelForm
from PROJ.apps.APP.models import UserProfile

.........
class ProfileForm(forms.ModelForm):
  class Meta:
      model = UserProfile
      exclude = ('user','last_name',)

from PROJ.apps.APP.models import UserProfile
проект и приложение где расположена расширенная модель

теперь в urls.py надо указать, что страницу редактирования профиля надо открывать с нужной нам формой

from PROJ.APP.forms import ProfileForm
........
    ('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),
    (r'^profiles/', include('profiles.urls')),
.......

Должно получиться примерно так.. проверяем на странице редактирования профиля (/profiles/edit/).

Теперь сделаем страницу отображения профиля profile/profile_detail.html

вывести можно что угодно из профиля вот так

<p><strong>Address 2:</strong><br>{{ profile.address2 }}</p>

где address2 это дополнительное поле
список пользователей с профилями можно вывести вот так

profiles/profile_list.html

{% extends "base.html" %}
{% block content %}
<h1>Список пользователей</h1>
<br/><br/>
    {% for p in object_list  %}
<a href="{% url profiles_profile_detail p.user %}">{{ p.user }}</a>
{% endfor %}
{% endblock %}

Теперь задача такая, сейчас профиль и пользователь живут почти своей жизнью, при регистрации пользователя профиль сам не создаётся это мы сейчас и решим сигналами.

в models.py добавим

from django.db.models import signals
from bucket.signals import create_profile
 
# When model instance is saved, trigger creation of corresponding profile
signals.post_save.connect(create_profile, sender=User)

создадим файл, если он не создан signals.py:

def create_profile(sender, instance, signal, created, **kwargs):
    """When user is created also create a matching profile."""

    from PROJ.apps.APP.models import UserProfile

    if created:
        UserProfile(user = instance).save()
        # Do additional stuff here if needed, e.g.
        # create other required related records

вот и всё, при регистрации нового пользователя автоматически будет создаваться к нему профиль, если у вас уже сейчас много пользователей, то создать для каждого профиль можно вот так

$ python manage.py shell
 
from django.contrib.auth.models import User
from bucket.models import Profile
 
users = User.objects.all()
for u in users:
     try:
          p = u.get_profile()
     except p.DoesNotExist:
          Profile(user = u).save()

ещё коснусь вопроса редактирования своего email в профиль, пока ещё не совсем разобрался с этим.

пока есть только пример, делается это анналогично через forms.py

class ProfileForm(ModelForm):
 
    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        try:
            self.fields['email'].initial = self.instance.user.email
            # self.fields['first_name'].initial = self.instance.user.first_name
            # self.fields['last_name'].initial = self.instance.user.last_name
        except User.DoesNotExist:
            pass
 
    email = forms.EmailField(label="Primary email",help_text='')
 
    class Meta:
      model = Profile
      exclude = ('user',)        
 
    def save(self, *args, **kwargs):
        """
        Update the primary email address on the related User object as well.
        """

        u = self.instance.user
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(ProfileForm, self).save(*args,**kwargs)
        return profile

надеюсь вернусь к этому вопросу

django + twitter

«Мопед не мой, я лишь объяву разместил»
У меня пока не хватает кармы, чтоб высылать за такие статьи инвайты на хабр, поэтому видимо кроме меня этого не жалает делать никто, то буду опубликовать тут. ибо информацию считаю полезной, не только для себя.

Доброго времени суток!
Появилась необходимость следующего характера: в движке на django, при публикации новости, чтоб создавался кросс-пост на twitter. Т.е. пишем новость, жмём сохранить и на твиттере появляется пост аля «Название статьи, bit.ly/s/ссылка_на_нашу_новость».

Для реализации потребовались следующие пакеты:

1. python-oauth2 | https://github.com/simplegeo/python-oauth2
2. oauth-python-twitter2 | http://code.google.com/p/oauth-python-twitter2/
3. simplejson | http://pypi.python.org/pypi/simplejson/
4. oauth2 | http://pypi.python.org/pypi/oauth2/
5. bitlyapi | http://pypi.python.org/pypi/bitlyapi/

1-4 пакеты нужны нам для связи с twitter’ом, 5-ый для укорачивания ссылок…
Перед тем как начать предполагаю, что у Вас есть аккаунт в twitter.com и bit.ly. Если нет, то это по минуте на регистрацию на каждом из сервисов.

Начнём с twitter’a…

При-python-им twitter

Проставляем все пакеты и выполняем следующие действия (кстати они описаны выше в руководстве python-oauth2):
Логинимся в браузере в twitter.com, и после этого переходим по ссылке http://twitter.com/apps/new. Далее мы воспользуемся инструкциями из замечательной статьи:

1. Заполняем все поля на свое усмотрение, для Application Type выбираем Client
2. Для Default Access type выбираем Read & Write, Use Twitter for login я оставил не выбранным
3. Вводим капчу и сабмитим

После этого twitter сгенерит CONSUMER_KEY, CONSUMER_SECRET.
Следующие два параметра нам вернет такой скрипт

from oauthtwitter import OAuthApi
import pprint

CONSUMER_KEY = "*****"
CONSUMER_SECRET = "*****"

twitter = OAuthApi(consumer_key, consumer_secret)
temp_credentials = twitter.getRequestToken()
print(twitter.getAuthorizationURL(temp_credentials))
oauth_verifier = raw_input('What is the PIN? ')
access_token = twitter.getAccessToken(temp_credentials, oauth_verifier)

print("oauth_token: " + access_token['oauth_token'])
print("oauth_token_secret: " + access_token['oauth_token_secret'])

В CONSUMER_KEY, CONSUMER_SECRET вводим значения полученные на предыдущем этапе, запускаем. Скрипт сгенерит url, переходим по нему и копируем PIN который нам вежливо предлагает twitter. Вставляем PIN в скрипт, он как раз его ждет.

На выходе мы получим значения для двух последних параметров OAUTH_TOKEN и OAUTH_TOKEN_SECRET.
Все, теперь у нас есть все что надо для oauth авторизации, осталось немного подправить скрипты.

Готовимся сокращать ссылки вместе с bit.ly

Предельно простой и удобный модуль bitlyapi. Схема простая:

1. Регистрируемся на bit.ly
2. Логинимся
3. Переходим по ссылке
4. Копируем username и api key

Правим модель в Django

Здесь мы не рассматриваем создание модели, предполагается, что Вы это умеете. В шапку добавляем:

#twitter
import oauth2 as oauth
import bitlyapi
bitly = bitlyapi.BitLy('username bitly', 'api key bitly') #вводим данные с прошлого шага
from oauthtwitter import OAuthApi
CONSUMER_KEY = "*****вводим свои данные*****"
CONSUMER_SECRET = "*****вводим свои данные*****"
OAUTH_TOKEN = "*****вводим свои данные*****"
OAUTH_TOKEN_SECRET = "*****вводим свои данные*****"
twitter = OAuthApi(CONSUMER_KEY, CONSUMER_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)

Переходим к моделе. Логика проста, есть boolean поле twitter_export, если галочка ставится, то новость публикуется при сохранении на твиттере. Если не понятно, то сейчас всё увидите и поймёте ))

class News(models.Model):
    title = models.CharField(max_length=300)
    content = models.TextField()
    twitter_export = models.BooleanField(default=False)

def save(self):
    link = bitly.shorten(longUrl='http://example.com/news/' + str(self.id) + '/') #формируем ссылку, которая будет сокращенна
    if self.twitter_export:   #если стоит галочка на twitter_export
        twit = u'%s... | Читать %s' % (self.title[:70], link['url'])  #формируем, что писать в твиттер.. учтите, что длина сообщения в твиттере 160 символов
        twitter.UpdateStatus(twit) #публикуем наш твит
        self.twitter_export = False #убираем галку twitter_export
    super(News, self).save()

Всё готово! Можно тестировать!

Источники

1. Пишем twitter-бота на python
2. Twitter Three-legged OAuth Example

django regroup пример

Сегодня Django снова порадовала, мне требовалось отобразить список категорий с под категориями
не важно можно с ul-li.
мне надо было завернуть всё это в красивый селект, ну вот что-то вроде этого.

в хтмл это примерно вот так

<SELECT NAME=browser>
    <OPTGROUP LABEL="Firefox">
      <OPTION LABEL="2.0 or higher">
        Firefox 2.0 or higher
      </OPTION>
      <OPTION LABEL="1.5.x">Firefox 1.5.x</OPTION>
      <OPTION LABEL="1.0.x">Firefox 1.0.x</OPTION>
    </OPTGROUP>
</select>

Допустим у меня есть 2 вот таких модели в вакууме.
Одна: категория – Firefox (как в примере выше.)

class Category(models.Model):
    name = models.CharField(max_length=100)
   
    def __unicode__(self):
            return self.name

И вторая не посредственно список, где есть поле указывающее на связь с родителем

class SubCategory(models.Model):
    category_name = models.ForeignKey(Category)
    name = models.CharField(max_length=200)
   
    def __unicode__(self):
            return self.name

Едем дальше.
в PHP, я бы в цикле прорисовки категорий поставил бы ещё один цикл выбирающий всех детей для данной категории. (хаха – много запросов ну и ладно)

Но тут у нас всё интереснее, в притом намного.
Мы во views должны сформировать что-то и отправить в шаблон уже как-то почти готовое для отображения.

Хочется городить велосипед, но тут Документация Django какбэ подсказывает

что есть такая хреновая, которая мне поможет.

Суть её в том, что тот массив что придёт к нам она может пересортироватьперегруппировать по любому полю, и поможет нам красиво его отобразить.

В итоге во views.py я делаю выборку того, что мне нужно.
Например вот так:

sub_category_list = SubCategory.objects.all()

И всё вот это мы отправляем тихо мирно в шаблон.
А в шаблоне нас встретит уже магический regroup

Вот с такой конструкцией

{% regroup sub_category_list by category_name as categ_list %}
     <select id="id_sub">
     <option value="">-----</option>
  {% for ca in categ_list %}
      <optgroup label="{{ca.grouper}}">
             {% for item in ca.list %}
          <option value="{{ item.id }}"> {{ item.name }}</option>
             {% endfor %}
      </optgroup>
  {% endfor %}
     </select>

Теперь по порядку.

{% regroup sub_category_list by category_name as categ_list %}

Группируем sub_category_list по полю category_name и называем результат categ_list
Дальше создаём html элемент, тут я думаю всё понятно.

и начинаем разбирать то, что у нас в categ_list

{% for ca in categ_list %}

Тут рассказывать ничего не надо, перебираем элементы по порядку

<optgroup label="{{ca.grouper}}">

Тут мы выводим то почему мы группировали {{ca.grouper}}

{% for item in ca.list %}

Вложенный цикл, обходим то, что внутри группы (тут нам помогает ca.list )

<option value="{{ item.id }}"> {{ item.name }}</option>

Собственно выводим всю эту красоту в конечный элемент

             {% endfor %}
      </optgroup>
  {% endfor %}

Вот и завершили все наши дела.
и получили нечто похожее на селект рутреккера тот, что в самом верху.

просто красиво и со вкусом.

django devserver статические файлы ( django devserver static files)

Во время разработки нужно чтоб статика обрабатывалась dev сервером, всякие файлы стилей js и т.п.

Для решения этой задачи я использую вот это:

urls.py

(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/home/django/list/static'}),

в корне проекта создаю каталог static, в котором создаю подкаталоги js images style и т.п

django-registration Пример

Мопед не мой, был в песочнице на хабре, потом в кэше гугла, чтоб совсем не потерялся решил оставить себе, был бы у меня инвайт, обязательно выслал, а так пока ещё не заслужил :(

В интернете есть много разрозненной информации о django-registration и о расширении стандартной модели Users. Однако, тотального примера, которое позволило бы новичку вникнуть как реализовать задуманную им логику регистрации нет. Я попробовал восполнить этот пробел.

Поставим себе следующие техническое задание:

* Во время регистрации пользователь заполняет дополнительные поля.
* При отправке формы — вся информация сохраняется в профайле, и на почту высылается письмо с ссылкой для активации.
* При переходе по ссылке пользователь активирует свою учетную запись, автоматически авторизуется на сайте, и перенаправляется на определенную страницу.

Сразу отметим что по умолчанию django-registration не включает в себя сохранение дополнительных полей, и после активация записи приходится вводить свой логин и пароль для авторизации.

Расширение модели User

Расширять модель пользователя мы будем соединением нашей модели Profile с стандартной моделью User посредством поля AutoOneToOneField. Подробнее об этом здесь.

Для начала создадим новое приложение profile:

django-admin.py startapp profile

Заходим в созданную папку profile, и открываем для редактирования файл models.py. В нашем примере, мы расширим модель пользователя полем проффесия, при чем посетитель сайта при регистрации будет выбирать проффесию из списка. Для удобного редактирования этого списка, мы вынесем его в отдельную модель Work. И так, models.py у нас обрастет таким кодом:

#profile/models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from habr.profile.fields import AutoOneToOneField

class Work(models.Model):
    work = models.CharField(max_length = 100, verbose_name = 'Работа')

class Profile(models.Model):
    user = AutoOneToOneField(User, related_name='profile', verbose_name=('User'), primary_key=True)
    work = models.ForeignKey(Work, verbose_name = 'Вид деятельности')

Мы подключаем поле AutoOneToOneField. Создадим в каталоге profile файл fields.py с таким кодом:

#profile/fields.py
# -*- coding: utf-8 -*-

from django.db.models import OneToOneField
from django.db.models.fields.related import SingleRelatedObjectDescriptor

class AutoSingleRelatedObjectDescriptor(SingleRelatedObjectDescriptor):
    def __get__(self, instance, instance_type=None):
    try:
        return super(AutoSingleRelatedObjectDescriptor, self).__get__(instance, instance_type)
    except self.related.model.DoesNotExist:
        obj = self.related.model(**{self.related.field.name: instance})
        obj.save()
    return obj

class AutoOneToOneField(OneToOneField):
    def contribute_to_related_class(self, cls, related):
        setattr(cls, related.get_accessor_name(), AutoSingleRelatedObjectDescriptor(related))

Осталось добавить приложение в INSTALLED_APPS (файл настроек settings.py), а также указать директиву AUTH_PROFILE_MODULE:

#settings.py
INSTALLED_APPS = (
##
'habr.profile',
##
)
AUTH_PROFILE_MODULE = 'habr.profile.profile'

Этот этап пройден, выполняем python manage.py syncdb и смотрим как создаются новые таблицы.

Прикручиваем и кастомизируем django-registration

Django-registration — это модуль который позволяет легко реализовать на сайте регистрацию с авторизацией по электронной почте (либо без онной). Достаточно подробно о её установке написано здесь, мы же пробежимся по основным нюансам, и займемся кастомизацией.

Для установки django-registration достаточно просто скачать её отсюда, разархивировать архив и положить папку registration в корневую папку вашего проекта. Однако, так поступать категорически не рекомендуется, лучше воспользоватся утилитами установки easy-install либо pip. Но тут есть один нюанс, если просто выполнить:

easy_install -Z django-registration

установится версия 0.7, которая не поддерживает очень удобный механизм — сигналы. Поэтому, чтобы посредством easy-install установить последнюю версию 0.8, необходимо выполнить:

easy_install-2.6 cdn.bitbucket.org/ubernostrum/django-registration/downloads/django-registration-0.8-alpha-1.tar.gz

(Возможно, когда вы будете читать эту статью, эта версия не будет последней актуальной, так что не забудьте проверить)
После этого следует добавить в INSTALLED_APPS — ‘registration’, а также обязательную директиву ACCOUNT_ACTIVATION_DAYS — которая устанавливает в течении скольки дней пользователь может активировать свою запись:

#settings.py
INSTALLED_APPS = (
##
'habr.profile',
'registration',
##
)
AUTH_PROFILE_MODULE = 'habr.profile.profile'

Далее необходимо сделать привязку URL, для этого редактируем файл urls.py, добавляя в urlpatterns строчку:

url(r'^accounts/', include('registration.backends.default.urls')),

Теперь для работы осталось только создать шаблоны для регистрации, активации и т.д. Это опять же подробно описано здесь, не будем повторятся.
После этого обновляем базу данных (python manage.py syncdb) и можно тестировать регистрацию. Не забудьте запустить почтовый сервер.

Теперь мы подошли к самому интерестному — добавим поля профиля в регистрационную форму, и несколько кастомизируем регистрацию.

Унасследуем стандартную форму модуля django-registration и добавим в неё наши поля. Создаем forms.py в папке profile:

#profile/forms.py
# -*- coding: utf-8 -*-

from django import forms
from registration.forms import RegistrationFormUniqueEmail
from habr.profile.models import Work

WORK_CHOICES = [
(work.id, work.work) for work in Work.objects.all()
]

class RegistrationFormProfile(RegistrationFormUniqueEmail):
    work = forms.ChoiceField(choices=WORK_CHOICES, label = 'Работа')

Обратите внимание как заполняется список выбора. При такой реализации при добавлении в таблицу Work новых строк прийдется перезагружать джангу, но зато список кешируется, и не будет происходить выполнения запроса к базе данных каждый раз при выводе формы. Такой вариант подходит когда таблицу не планируется часто изменять.

Теперь нам надо чтобы бэкэнд django-registration использовал нашу форму вместо своей по умолчанию, для этого в urls.py перед

url(r'^accounts/', include('registration.backends.default.urls')),

добавим

url(r'^accounts/register/$', 'registration.views.register', {'backend': 'registration.backends.default.DefaultBackend', 'form_class':RegistrationFormProfile,},
name='registration_register'),

И имортируем форму:

from habr.profile.forms import RegistrationFormProfile

Раньше приходилось переписывать у формы метод save(), в django-registration 0.8 появилась возможность повесить сигнал на создание юзера. Также сразу повесим сигнал на активацию юзера, для того чтобы сразу провести авторизацию.

Создадим signals.py в папке profile:

#profile/signals.py
# -*- coding: utf-8 -*-

from registration.signals import user_registered, user_activated
from habr.profile.models import Profile
from forms import RegistrationFormProfile
from django.contrib import auth

def user_created(sender, user, request, **kwargs):
    form = RegistrationFormProfile(request.POST)
    profile = Profile(user=user, work_id = int(form.data['work']))
    profile.save()

def login_on_activation(sender, user, request, **kwargs):
    user.backend='django.contrib.auth.backends.ModelBackend'
    auth.login(request,user)

user_activated.connect(login_on_activation)
user_registered.connect(user_created)

Теперь осталась импортировать эти функции в urls.py:

from habr.profile.signals import *

Остался последний штрих — мы хотим чтобы после регистрации пользователь попадал на определенную целевую страницу, для этого можно просто повесить представление на url accounts/activate/complete/:

url(r'^accounts/activate/complete/$', 'gamb.profile.views.redirect_after_activation'),

Техническое задание выполнено!

Страница 1 из 212