django 评论

Django comments评论

安装

Django comments are deprecated in latest version. You can either install older django version (such as 1.5). Or the better solution is to install comments from external repository like so:

pip install django-contrib-comments

and then change all imports like this:

from django.contrib.comments.models import Comment # old
from django_comments.models import Comment # new

引入app的时候也会出现相关的问题,需要引入两个包

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'django_comments',
'blog',
'pagination',
'tagging',
]

需要单独的引入django.contrib.sites 和 django_comments 。 注意这里必须是django_comments 。 如果引入 django.contrib.comments 则会报错。

都引入完成后需要进行一次 manage.py migrate

使用comments

配置urls.py中的变量让comments 添加

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include('blog.urls', namespace='blog')),
    url(r'^comments/', include('django_comments.urls')),
]

注意这里django_comments 的引入包不一样,mac下面需要使用这种引入方式。

这里的引入要引入到外层url里面,如果引入到里层url会报错,这一点要特别注意。

在setting.py 中 添加SITE_ID变量

SITE_ID = 1

测试一个添加comments的form

首先要在页面里面添加标签引入

{% load comments %}

然后添加一个评论form作为测试

{% render_comment_form for post %}

可以看到绑定到了post上了,后续看看如何表示

对comments进行样式添加和表示

如果采用上面的方式表示comments,只能在样式表里面根据他自带的样式来进行修改。或者改默认的site里面的模板才能修改样式,但是这种方式很多情况下面不会适用,所以我们可以才用自定义form的方式来进行form 的封装

封装方式:

<div class="separator"></div>
<h3>{% trans 'Send Us a Message' %}</h3>
<p>{% trans 'message content' %}</p>
                
{% get_comment_form for post as form %}
<div id="contactform">
  <form id="content" action="{% comment_form_target %}" method="post">
    {% csrf_token %}
    <label for="name" id="name_label">Name (required)</label>
    <input type="text" name="name" id="id_name" size="50" value="" class="text-input" />
    <label for="email" id="email_label">URL (required)</label>
    <input type="text" name="url" id="id_url" size="50" value="" class="text-input" />
    <label for="email" id="email_label">Email (required)</label>
    <input type="text" name="email" id="id_email" size="50" value="" class="text-input" />
    <label for="msg" id="msg_label">Message</label>
    <textarea rows="10" cols="40" name="comment" id="id_comment" class="text-input" maxlength="3000"></textarea>
    <br/>
    {{ form.content_type }}
    {{ form.object_pk }}
    {{ form.timestamp }}
    {{ form.security_hash }}
    <input type="submit" class="button" id="submit_btn" name="submit" value="Send Message"/>
    <input type="hidden" name="next" value="{% url 'blog:single' post.id %}" />
  </form>
  <div class="clear"></div>
</div>

可以将相应的标签命名为django-comments 需要的方式,根据自己的需要填充。
最上面的 {% get_comment_form for post as form %} 用于绑定评价到某个具体的对象上面。可以在评价对象里面看到评价值绑定到id上,所以没有办法实现动态的多对象绑定,我们需要将对象绑定在某一组对象上,或者我们将需要绑定的对象分开,才能实现在不同的对象上绑定。

下面的绑定绑到具体的id_name之类的上面就可以了,这样我们就可以添加相关的class来匹配相关的页面了。

在后面记得添加上默认的隐藏的对象,来把对象id、对象时间等添加到form里面。

最后一行<input type="hidden" name="next" value="{% url 'blog:single' post.id %}" /> 用于绑定回复之后的跳转。

2016/5/19 posted in  python

django 分页

django 分页 pagination

使用django pagination的文档比较多,但是完整整理的还是比较少的,这里完整整理一下使用django pagination的过程。

安装 django pagination

默认的pagination包不包含在django安装包里面的,所以如果直接配置,然后再使用会报找不到pagination类的错误,如下No module named pagination。

安装方式 sudo pip install django-pagination.

详情可参考这篇文章

配置分页程序

可以参考官方的pagination文档
也可以参考七步教你实现Django网站列表自动分页

这一步没有什么问题,按照官方指导配置即可。

django 1.9版本报新的错误

django1.9使用django-pageination出现AttributeError错误处理

问题:
在django1.9使用django-pageination出现错误

AttributeError: 'WSGIRequest' object has no attribute 'REQUEST'

原因:
HttpRequest.REQUEST在django1.7中被弃用并使用更明确的HttpRequest.GET和HttpRequest.POST来替代,而在django1.9中HttpRequest.REQUEST被移除了。

处理:

打开python安装目录下的dist-packages_pagination_middleware.py

把

return int(self.REQUEST['page'])

修改成

return int(self.POST['page'])

遗留一个问题

至此分页已经OK,但是遗留一个分页菜单的问题,光使用{% paginate %} 会报错

TypeError: sequence index must be integer, not 'slice

详细看了一下源码,在下面地方找到问题

...
try:
    paginator = context['paginator']
    page_obj = context['page_obj']
    page_range = paginator.page_range
    # Calculate the record range in the current page for display.
    records = {'first': 1 + (page_obj.number - 1) * paginator.per_page}
    records['last'] = records['first'] + paginator.per_page - 1
    if records['last'] + paginator.orphans >= paginator.count:
        records['last'] = paginator.count
    # First and last are simply the first *n* pages and the last *n* pages,
    # where *n* is the current window size.
    first = set(list(page_range)[:window])
    last = set(list(page_range)[-window:])
    # Now we look around our current page, making sure that we don't wrap
    # around.
    current_start = page_obj.number-1-window
    if current_start < 0:
        current_start = 0
    current_end = page_obj.number-1+window
    if current_end < 0:
        current_end = 0
    current = set(list(page_range)[current_start:current_end])
    pages = []
...

默认的代码是

    first = set(page_range[:window])
    last = set(page_range[-window:])

这个在当前版本是不能执行的,所以修改为上面的就好了。

django 1.9的 paginate。

django 1.9版本用原始的配置发现可以进行分页,但是按页数显示不同的是有问题的,应该是django1.9版本的升级导致的,也暂时没有看源码,直接修改用官方给出的表现方式来配置。

前台代码为:

{% block maincontent %}
    <section class="content">
    
        {% if latest_entry_list %}
            {% for entry in latest_entry_list %}
            
        <article class="post">
            <div class="date-wrapper"> 
                <div class="date-value">{{ entry.publication_date.day }}</div>
                <div class="month-value">{{ entry.publication_date.month }}</div>
            </div>
            <div class="header-wrapper">
                <h2 class="posttitle"><a href="single.html">{{entry.title}}</a></h2>
                <div class="entry-utility">
                    <div class="user">Posted by <a href="#">Templatesquare</a></div> <div class="tag">Tags: <a href="#">Wordpress</a></div>
                </div>
            </div>
            <div class="clear"></div>
            <div class="postimg">
                <img src="{{entry.head_image_url}}" alt="" />
            </div>
   
                                <div class="entry-content">
                {{ entry.blog_abstract }}
                <a href="single.html" class="more">更多&rarr;</a>
            </div>
           
            <div class="clear"></div>
        </article>
            {% endfor %}
        {% endif %}
        
        <div class="wp-pagenavi">
            <span class="step-links">
                <span class="pages">
                    Page {{ latest_entry_list.number }} of {{ latest_entry_list.paginator.num_pages }}
                </span>
                {% if latest_entry_list.has_previous %}
                    <a href="?page={{ latest_entry_list.previous_page_number }}">{% trans 'previous' %}</a>
                {% endif %}
                {% for pagenum in latest_entry_list.paginator.page_range %}
                    {% if pagenum == latest_entry_list.number %}
                        <span class="current"><span>{{ pagenum }}</span></span>
                    {% else %}
                        <a class="page" href="?page={{ pagenum }}">{{ pagenum }}</a>
                        {% endif %}
                {% endfor %}
                {% if latest_entry_list.has_next %}
                    <a href="?page={{ latest_entry_list.next_page_number }}">{% trans 'next' %}</a>
                {% endif %}
            </span>
        </div>
    </section>
{% endblock %}  

后台view代码

def blog(request):
    latest_entry_list = Entry.objects.order_by('creation_date')
    paginator = Paginator(latest_entry_list, 5)
    page = request.GET.get('page')
    
    try:
        entrys = paginator.page(page)
    except PageNotAnInteger:
        entrys = paginator.page(1)
    except EmptyPage:
        entrys = paginator.page(paginator.num_pages)
    context = {'latest_entry_list':entrys}
    
    return render(request, 'blog.html', context)

这样分页就完成了。

2016/5/18 posted in  python

django 国际化详解与源码分析

django 国际化

调试国际化环境

首次按照教程直接执行,在windows或者mac下面都会报相关的错误,需要我们安装gettext包。否则会报错

CommandError:Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed

此时表示gettext环境没有安装,在mac下需要安装brew之后安装gettext。

安装步骤如下:

  1. Install Homebrew : _usr_bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  2. Install GNU gettext : brew install gettext
  3. Create symlink : brew link gettext --force

安装完成后,gettext的默认是不会加到环境变量中的,需要我们手动添加到相关环境变量

export PATH=$PATH:/usr/local/Cellar/gettext/0.19.5.1/bin/

mac用户建议将这个加到开机的rc.local中去,否则每次都需要重新配置,比较麻烦

详情可以参考这个文档
http://stackoverflow.com/questions/23353113/django-admin-py-makemessages-not-working

这样安装完成后就可以在文档目录下面调用了,但是此时直接运行python django-admin.py makemessages ‐l zh_CN不会有任何反应。

解决makemessages运行问题

进到/Library/Python/2.7/site-packages/Django-1.9.6-py2.7.egg/django/core/management/commands里面可以查看到makemessages.py相应的源文件,仔细的读一下代码,发现其中的问题

可以看到最后的生成文件的方法是write_po_file(), 但是这个方法实际上是没有掉到的,往回跟可以发现问题出现在

def build_potfiles(self):
    """
    Build pot files and apply msguniq to them.
    """
    file_list = self.find_files(".")
    self.remove_potfiles()
    self.process_files(file_list)
    potfiles = []
    for path in self.locale_paths:
        potfile = os.path.join(path, '%s.pot' % str(self.domain))
        if not os.path.exists(potfile):
            continue
        args = ['msguniq'] + self.msguniq_options + [potfile]
        msgs, errors, status = gettext_popen_wrapper(args)
        if errors:
            if status != STATUS_OK:
                raise CommandError(
                    "errors happened while running msguniq\n%s" % errors)
            elif self.verbosity > 0:
                self.stdout.write(errors)
        with io.open(potfile, 'w', encoding='utf-8') as fp:
            fp.write(msgs)
        potfiles.append(potfile)
    return potfiles

这个方法里面,可以看到程序会去搜索一个pot之类的后缀的文件,如果locale下没有任何文件的话程序会直接continue到最后,既不能生成任何的文件。所以在locale下面需要放置一个文件,然后在执行python django-admin.py makemessages ‐l zh_CN就可以生成。

一些原理

问题解决的一些原理分享

  1. 默认使用第一次只会生成相应的po文件,po文件会去搜索当前目录下面的所有文件中的国际化字段,然后在该文件中自动添加,也会添加相关注释告知相应的国际化文件属于哪个文件,方便翻译。
  2. 需要在默认的middleware_classes配置文件下添加配置才可以生效。

生效代码:

MIDDLEWARE_CLASSES = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
  1. 当修改完成配置之后可以再执行django-admin.py compilemessages , 将po文件编译成为mo文件。
  2. 测试方法,使用chrome,设置——高级设置——语言——语言和输入设置,将英文放到最上面去即可,参考

  3. 相关的配置可以参考几个网站

http://www.cnblogs.com/oubo/archive/2012/04/05/2433690.html
http://www.oschina.net/question/12_15775?fromerr=JlvW0sS9
官方文档

特别注意

django 1.9版本的中文国家化名称是 zh_Hans , 不在是以前的zh_cn。
后面配置的时候需要注意。

locale目录需要配置在自己的当前目录下面,每个APP可以有自己的国际化目录。

2016/5/17 posted in  python

django model模型

django model

django model syntex

字段类型,主要确定以下信息:
  • 数据库当中的列类型 (比如: INTEGER, VARCHAR)。
  • 渲染表单时使用的默认HTML 部件(例如,, )。
  • 最低限度的验证需求,它被用在 Django 管理站点和自动生成的表单中。
字段选项

null : 为True表示将用null 来存储空值
blank:
choice:
default:
help_text:
primary_key:
unique:

字段的自述名

一般情况下自述名放在第一个参数,使用字符串类型。

first_name = models.CharField("person's first name", max_length=30)

默认情况下不加字符串表示和变量名相同
在外键的时候由于要求第一个字段是外键类型名,所以需要加一个verbose_name 参数

poll = models.ForeignKey(Poll, verbose_name="the related poll")
sites = models.ManyToManyField(Site, verbose_name="list of sites")
关系
多对一关系:

ForeignKey(). ForeignKey 需要一个位置参数:与该模型关联的类。
若要创建一个递归的关联 —— 对象与自己具有多对一的关系 —— 请使用models.ForeignKey('self')。

多对多关系:

多对多关系: ManyToManyField 需要一个位置参数:和该模型关联的类。
在哪个模型中设置 ManyToManyField 并不重要,在两个模型中任选一个即可 —— 不要两个模型都设置。
Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。源模型的ManyToManyField 字段将使用through 参数指向中介模型。

from django.db import models    
class Person(models.Model):
    name = models.CharField(max_length=128) 
    def __str__(self):              # __unicode__ on Python 2
        return self.name    
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')  
    def __str__(self):              # __unicode__ on Python 2
        return self.name    
class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

中介模型有一些限制:

  • 中介模型必须有且只有一个外键到源模型(上面例子中的Group),或者你必须使用ManyToManyField.through_fields 显式指定Django 应该在关系中使用的外键。如果你的模型中存在不止一个外键,并且through_fields没有指定,将会触发一个无效的错误。 对目标模型的外键有相同的限制(上面例子中的 Person)。
  • 对于通过中介模型与自己进行多对多关联的模型,允许存在到同一个模型的两个外键,但它们将被作为多对多关联关系的两个(不同的)方面。如果有超过 两个外键,同样你必须像上面一样指定through_fields,否则将引发一个验证错误。
  • 使用中介模型定义与自身的多对多关系时,你必须设置 symmetrical=False(详见模型字段参考)。
一对一关系:

OneToOneField要一个位置参数:与模型关联的类。

from django.db import models    
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)   
    def __str__(self):              # __unicode__ on Python 2
        return "%s the place" % self.name   
class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)   
    def __str__(self):              # __unicode__ on Python 2
        return "%s the restaurant" % self.place.name    
class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)  
    def __str__(self):              # __unicode__ on Python 2
        return "%s the waiter at %s" % (self.name, self.restaurant)
跨文件模型

可以定义跨文件的模型结构,在顶部import之后就可以在内部进行跨文件的模型内容。

模型的方法

可以在模型上使用自定义的方法来给对象添加底层的功能,Manager 方法用于“表范围”的事务,模型的方法应该着眼于特定的模型实例。

from django.db import models    
class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField() 
    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"    
    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

也可以覆盖部分模型的自定义方法,如__str()__ 方法,__unicode()__ 方法、get_absolute_url方法等。

其中__str()__用于python3中,相当于python2 的 __unicode()__方法,主要用于覆盖对象的某些打印值。
get_absolute_url()方法告诉Django 如何计算一个对象的URL。Django 在它的管理站点中使用到这个方法,在其它任何需要计算一个对象的URL 时也将用到。

任何具有唯一标识自己的URL 的对象都应该定义这个方法。

重写预定于的模型方法

还有另外一部分封装数据库行为的模型方法,你可能想要自定义它们。特别是,你将要经常改变save() 和delete() 的工作方式。

可以通过覆盖这些方法来改变默认的行为。

from django.db import models    
class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()    
    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()

这个模型表示了可以在调用save方法的时候做一些其他的事情。
super(Blog, self).save(*args, **kwargs) 这个方法将数据保存到数据库中,

支持执行自定义SQL

在模型方法和模型级别方法中可以执行自订的的SQL,

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print(p)
John Smith
Jane Jones
模型继承

自定义的模型类应该继承django.db.models.Model。
需要决定的是继承的模型类是否需要进行数据库持久化

在Django 中有3种风格的继承。

  • 通常,你只想使用父类来持有一些信息,你不想在每个子模型中都敲一遍。这个类永远不会单独使用,所以你要使用抽象基类。
  • 如果你继承一个已经存在的模型且想让每个模型具有它自己的数据库表,那么应该使用多表继承。
  • 最后,如果你只是想改变一个模块Python 级别的行为,而不用修改模型的字段,你可以使用代理模型。
抽象基类

将一些共有的信息放进Model的时候,抽象化类是比较有用的。

from django.db import models    
class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField() 
    class Meta:
        abstract = True 
class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

设置meta类中的abstract属性为True,就实现了模型的抽象化基类

多表继承

使用这种继承方式时,每一个层级下的每个 model 都是一个真正意义上完整的 model。 每个Model 都会拥有一个专属的数据表,都可以创建和查询数据表。

from django.db import models    
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)   
class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

可以看到这种模式就是直接继承。

代理继承

有时,你可能只想更改 model 在 Python 层的行为实现。比如:更改默认的 manager ,或是添加一个新方法。

代理继承要做的:为原始模型创建一个代理 。你可以创建,删除,更新代理 model 的实例,而且所有的数据都可以像使用原始 model 一样被保存。 不同之处在于:你可以在代理 model 中改变默认的排序设置和默认的 manager ,更不会对原始 model 产生影响。

from django.db import models    
class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30) 
class MyPerson(Person):
    class Meta:
        proxy = True    
    def do_something(self):
        # ...
        pass

设置Meta类中 proxy 的值为 True,就完成了对代理 model 的声明。

2016/5/14 posted in  python