django 模型关系

django模型关系

多对一关系

Django 使用 django.db.models.ForeignKey 定义多对一关系。

几种特殊的外键模式:

  • 若要创建一个递归的关联 —— 对象与自己具有多对一的关系 —— 请使用models.ForeignKey('self')。
  • 如果你需要关联到一个还没有定义的模型,你可以使用模型的名字而不用模型对象本身。
  • 若要引用在其它应用中定义的模型,你可以用带有完整标签名的模型来显式指定。在解析两个应用之间具有相互依赖的导入时,这种引用将会很有帮助。

ForeignKey 会自动创建数据库索引。你可以通过设置db_index 为False 来取消。如果你创建外键是为了一致性而不是用来Join,或者如果你将创建其它索引例如部分或多列索引,你也许想要避免索引的开销。

ForeignKey的字段
ForeignKey.limit_choices_to

当这个字段使用模型表单或者Admin 渲染时(默认情况下,查询集中的所有对象都可以使用),为这个字段设置一个可用的选项。

这个字段可以是boolean型的,进行boolean过滤,或者是一个函数,进行函数的过滤。看下面两个例子:

    staff_member = models.ForeignKey(User, limit_choices_to={'is_staff': True})

例子1,将使得模型表单 中对应的字段只列出is_staff=True 的Users。

    def limit_pub_date_choices():
        return {'pub_date__lte': datetime.date.utcnow()}
    limit_choices_to = limit_pub_date_choices

这个例子对选择时间进行限制

如果limit_choices_to使用可调用对象,这个可调用对象将在每次创建一个新表单的时候都调用。它还可能在一个模型校验的时候调用,例如被管理命令或者Admin。Admin 多次构造查询集来验证表单在各种边缘情况下的输入,所以你的可调用对象可能调用多次。

ForeignKey.related_name

这个名称用于让关联的对象反查到源对象。

如果你不想让Django 创建一个反向关联,请设置related_name 为 '+' 或者以'+' 结尾

ForeignKey.related_query_name

这个名称用于目标模型的反向过滤。如果设置了related_name,则默认为它的值,否则默认值为模型的名称:

    # Declare the ForeignKey with related_query_name
    class Tag(models.Model):
        article = models.ForeignKey(Article, related_name="tags", related_query_name="tag")
        name = models.CharField(max_length=255)
    # That's now the name of the reverse filter
    Article.objects.filter(tag__name="important")
ForeignKey.to_field

关联到的关联对象的字段名称。默认地,Django 使用关联对象的主键。

ForeignKey.db_constraint

控制是否在数据库中为这个外键创建约束。

ForeignKey.on_delete¶

当一个ForeignKey 引用的对象被删除时,Django 默认模拟SQL 的ON DELETE CASCADE 的约束行为,并且删除包含该ForeignKey的对象。

CASCADE
级联删除;默认值。

PROTECT
抛出ProtectedError 以阻止被引用对象的删除,它是django.db.IntegrityError 的一个子类。

SET_NULL
把ForeignKey 设置为null; null 参数为True 时才可以这样做。

SET_DEFAULT
ForeignKey 值设置成它的默认值;此时必须设置ForeignKey 的default 参数。

SET()
设置ForeignKey 为传递给SET() 的值,如果传递的是一个可调用对象,则为调用后的结果。

DO_NOTHING
不采取任何动作。

2016/6/8 posted in  python

django 模型字段

django模型字段

关注django模型字段主要关注模型的几个方面

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

几个默认字段

null

如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

blank

如果为True,该字段允许不填。默认为False。

choice

由二项元组构成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

    from django.db import models
    class Person(models.Model):
        SHIRT_SIZES = (
            ('S', 'Small'),
            ('M', 'Medium'),
            ('L', 'Large'),
        )
        name = models.CharField(max_length=60)
        shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
default

字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。

help_text

表单部件额外显示的帮助内容,会添加到默认的Form中。即使字段不在表单中使用,它对生成文档也很有用。

primary_key

如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField 字段做为主键.

主键字段是只读的。如果你在一个已存在的对象上面更改主键的值并且保存,一个新的对象将会在原有对象之外创建出来。

unique

如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

这是一个在数据库级别的强制性动作,并且通过模型来验证。如果你试图用一个重复的值来保存unique 字段,那么一个django.db.IntegrityError就会通过模型的save() 函数抛出来。

注意当设置unique 为True 时,你不需要再指定 db_index,因为unique 本身就意味着一个索引的创建。

db_column

数据库中用来表示该字段的名称。如果未指定,那么Django将会使用Field名作为字段名.

dh_index

若值为 True, 则 django-admin sqlindexes 将会为此字段输出 CREATE INDEX 语句。创建索引

db_tablespace

如果该字段有索引的话,database tablespace的名称将作为该字段的索引名。 如果DEFAULT_INDEX_TABLESPACE 已经设置,则默认值是由DEFAULT_INDEX_TABLESPACE指定, 如果没有设置则由 db_tablespace 指定,如果后台数据库不支持表空间,或者索引,则该选项被忽略

editable

如果设为False, 这个字段将不会出现在 admin 或者其他 ModelForm. 他们也会跳过 模型验证. 默认是True.

error_messages

error_messages 参数能够让你重写默认抛出的错误信息。通过指定 key 来确认你要重写的错误信息。

error_messages 的 key 值包括 null, blank, invalid, invalid_choice, unique, 和 unique_for_date.

unique_for_date

Field.unique_for_date
当设置它为DateField 和DateTimeField 字段的名称时,表示要求该字段对于相应的日期字段值是唯一的。

例如,你有一个title 字段设置unique_for_date="pub_date",那么Django 将不允许两个记录具有相同的title 和pub_date。

注意,如果你将它设置为DateTimeField,只会考虑其日期部分。此外,如果USE_TZ 为True,检查将以对象保存时的当前的时区 进行。

这是在模型验证期间通过Model.validate_unique() 强制执行的,而不是在数据库层级进行验证。如果unique_for_date 约束涉及的字段不是ModelForm(例如,exclude中列出的字段或者设置了editable=False),Model.validate_unique() 将忽略该特殊的约束。

unique_for_month
unique_for_year
verbose_name

一个字段的可读性更高的名称。如果用户没有设定冗余名称字段,Django会自动将该字段属性名中的下划线转换为空格,并用它来创建冗余名称。

validators

该字段将要运行的一个Validator 的列表。

模型类型

自增字段

class AutoField(options)

一个根据实际ID自动增长的IntegerField 。你通常不需要直接使用;如果不指定,一个主键字段将自动添加到你创建的模型中。

BigIntegerField

class BigIntegerField([options])

一个64位整数, 类似于一个 IntegerField ,它的值的范围是 -9223372036854775808 到9223372036854775807之间. 这个字段默认的表单组件是一个TextInput.

BinaryField

class BinaryField([options])
这是一个用来存储原始二进制码的Field. 只支持bytes 赋值,注意这个Field只有很有限的功能。例如,不大可能在一个BinaryField 值的数据上进行查询

Abusing BinaryField

尽管你可能想使用数据库来存储你的文件,但是99%的情况下这都是不好的设计。这个字段 不是替代static files 的合理操作.

BooleanField

class BooleanField(options)
一个 true/false 字段。

此字段的默认表单挂件是一个CheckboxInput.

如果你需要设置null 值,则使用NullBooleanField 来代替BooleanField。

如果Field.default没有指定的话, BooleanField 的默认值是 None。

CharField

class CharField(max_length=None[, options])

一个用来存储从小到很大各种长度的字符串的地方

如果是巨大的文本类型, 可以用 TextField.

这个字段默认的表单样式是 TextInput.

CharField必须接收一个额外的参数:

CharField.max_length
字段的最大字符长度.max_length将在数据库层和Django表单验证中起作用, 用来限定字段的长度.

CommaSeparatedIntegerField

class CommaSeparatedIntegerField(max_length=None[, options])

一个逗号分隔的整数字段。像 CharField一样, 需要一个max_length 参数, 同时数据库移植时也需要注意。

DateField

class DateField([auto_now=False, auto_now_add=False, options])

这是一个使用Python的datetime.date实例表示的日期. 有几个额外的设置参数:

DateField.auto_now
每次保存对象时,自动设置该字段为当前时间。用于"最后一次修改"的时间戳。注意,它总是使用当前日期;和你可以覆盖的那种默认值不一样。

DateField.auto_now_add
当对象第一次被创建时自动设置当前时间。用于创建时间的时间戳. 它总是使用当前日期;和你可以覆盖的那种默认值不一样。

该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮.包含了一个额外的invalid_date错误消息键.

auto_now_add, auto_now, and default 这些设置是相互排斥的. 他们之间的任何组合将会发生错误的结果.

DateTimeField

class DateTimeField([auto_now=False, auto_now_add=False, options])

它是通过Python datetime.datetime实例表示的日期和时间. 携带了跟DateField一样的额外参数.

该字段默认对应的表单控件是一个单个的TextInput(单文本输入框). 管理界面是使用两个带有 JavaScript控件的 TextInput 文本框.

DecimalField

class DecimalField(max_digits=None, decimal_places=None[, options])

用python中 Decimal 的一个实例来表示十进制浮点数. 有两个 必须的参数:

DecimalField.max_digits
位数总数,包括小数点后的位数。该值必须大于等于decimal_places.

DecimalField.decimal_places
小数点后的数字数量

例如,要保存最大为 999 并有两位小数的数字,你应该使用:

    models.DecimalField(..., max_digits=5, decimal_places=2)

而要存储那些将近10亿,并且要求达到小数点后十位精度的数字:

    models.DecimalField(..., max_digits=19, decimal_places=10)

该字段默认的窗体组件是 TextInput.

DurationField

class DurationField([options])

用作存储一段时间的字段类型 - 类似Python中的timedelta. 当数据库使用的是PostgreSQL, 该数据类型使用的是一个 interval 而在Oracle上,则使用的是 INTERVAL DAY(9) TO SECOND(6). Otherwise a bigint of microseconds is used.

EmailField

class EmailField([max_length=254, options])

一个 CharField 用来检查输入的email地址是否合法。它使用 EmailValidator 来验证输入合法性。

FileField

class FileField([upload_to=None, max_length=100, options])

一个上传文件的字段。

注意

FileField字段不支持primary_key 和unique参数,如果使用会生成 TypeError错误

有两个可选参数:

FileField.upload_to
Changed in Django 1.7:
在旧版本Django中,upload_to 属性是必须要有的;
一个本地文件系统的路径,它将附加到MEDIA_ROOT 设置的后面来确定url 属性的值。

这个路径可以会包含一个strftime() 格式串,它将在文件上传时被替换为实际的日期/时间(这样上传的文件就不会塞满你指定的文件夹)。

它还可以是一个可调用对象如函数,将调用它来获取上传路径,包括文件名。这个可调用对象必须接受两个参数,并且返回一个Unix 风格的路径(带有前向/)给存储系统。将传递的两个参数为:

Argument Description
instance
FileField 定义所在的模型的实例。更准确地说,就是当前文件的所在的那个实例。

大部分情况下,这个实例将还没有保存到数据库中,若它用到默认的AutoField 字段,它的主键字段还可能没有值。

filename The filename that was originally given to the file. This may or may not be taken into account when determining the final destination path.
FileField.storage¶
一个Storage 对象,用于你的文件的存取。参见管理文件 获取如何提供这个对象的细节。

这个字段的默认表单Widget 是ClearableFileInput。

在模型中调用FileField 或 ImageField (见下方) 需如下几步:

在你的settings文件中, 你必须要定义 MEDIA_ROOT 作为Django存储上传文件的路径(从性能上考虑,这些文件不能存在数据库中。) 定义一个 MEDIA_URL 作为基础的URL或者目录。确保这个目录可以被web server使用的账户写入。
在模型中添加FileField 或 ImageField 字段, 定义 upload_to参数,内容是 MEDIA_ROOT 的子目录,用来存放上传的文件。
数据库中存放的仅是这个文件的路径 (相对于MEDIA_ROOT). 你很可能会想用由Django提供的便利的url 属性。比如说, 如果你的ImageField 命名为 mug_shot, 你可以在template中用 {{ object.mug_shot.url }}获得你照片的绝对路径。
例如,如果你的 MEDIA_ROOT设定为 '_home_media',并且 upload_to设定为 'photos_%Y_%m_%d'。 upload_to的'%Y_%m_%d'被strftime()所格式化;'%Y' 将会被格式化为一个四位数的年份, '%m' 被格式化为一个两位数的月份'%d'是两位数日份。如果你在Jan.15.2007上传了一个文件,它将被保存在_home_media_photos/2007/01/15目录下.

如果你想获得上传文件的存盘文件名,或者是文件大小,你可以分别使用 name 和 size 属性; 更多可用属性及方法信息,请参见 File 类索引 和 Managing files 主题指导.

Note

保存的文件作为模型存储在数据库中的一部分,所以在磁盘上使用的实际的文件名在模型保存完毕之前是不可靠的。

上传的文件对应的URL可以通过使用 url 属性获得. 在内部,它会调用 Storage 类下的url()方法.

值得注意的是,无论你在任何时候处理上传文件的需求,你都应该密切关注你的文件将被上传到哪里,上传的文件类型,以避免安全漏洞。认证所有上传文件 以确保那些上传的文件是你所认为的文件。例如,如果你盲目的允许其他人在无需认证的情况下上传文件至你的web服务器的root目录中,那么别人可以上传一个CGI或者PHP脚本然后通过访问一个你网站的URL来执行这个脚本。所以,不要允许这种事情发生。

甚至是上传HTML文件也值得注意,它可以通过浏览器(虽然不是服务器)执行,也可以引发相当于是XSS或者CSRF攻击的安全威胁。

FileField 实例将会在你的数据库中创建一个默认最大长度为100字符的varchar 列。就像其他的fields一样, 你可以用 max_length 参数改变最大长度的值.

FileField 和FieldFile¶

class FieldFile[source]¶
当你添加 FileField 到你的模型中时, 你实际上会获得一个 FieldFile的实例来替代将要访问的文件。 除了继承至 django.core.files.File的功能外, 这个类还有其他属性和方法可以用于访问文件:

FieldFile.url¶
通过潜在Storage 类的url()方法可以只读地访问文件的URL。

FieldFile.open(mode='rb')[source]¶
该方法像标准的Python open() 方法,并可通过 mode参数设置打开模式.

FieldFile.close()[source]¶
该方法像标准的Pythonfile.close() 方法,并关闭相关文件.

FieldFile.save(name, content, save=True)[source]¶
这个方法会将文件名以及文件内容传递到字段的storage类中,并将模型字段与保存好的文件关联. 如果想要手动关联文件数据到你的模型中的 FileField实例, 则save() 方法总是用来保存该数据.

Takes two required arguments: name which is the name of the file, and content which is an object containing the file’s contents. The optional save argument controls whether or not the model instance is saved after the file associated with this field has been altered. Defaults to True.

Note that the content argument should be an instance of django.core.files.File, not Python’s built-in file object. You can construct a File from an existing Python file object like this:

from django.core.files import File

Open an existing file using Python's built-in open()

f = open('_tmp_hello.world')
myfile = File(f)
Or you can construct one from a Python string like this:

from django.core.files.base import ContentFile
myfile = ContentFile("hello world")
更多信息, 请参见 Managing files.

FieldFile.delete(save=True)[source]¶
Deletes the file associated with this instance and clears all attributes on the field. Note: This method will close the file if it happens to be open when delete() is called.

The optional save argument controls whether or not the model instance is saved after the file associated with this field has been deleted. Defaults to True.

注意,model删除的时候,与之关联的文件并不会被删除。如果你要把文件也清理掉,你需要自己处理。

FilePathField

class FilePathField(path=None[, match=None, recursive=False, max_length=100, options])

一个 CharField ,内容只限于文件系统内特定目录下的文件名。有三个参数, 其中第一个是 必需的:

FilePathField.path
必要的。The absolute filesystem path to a directory from which this FilePathField should get its choices. 例如: "_home_images".

FilePathField.match
可选的.A regular expression, as a string, that FilePathField will use to filter filenames. Note that the regex will be applied to the base filename, not the full path. Example: "foo.*.txt$", which will match a file called foo23.txt but not bar.txt or foo23.png.

FilePathField.recursive
Optional. Either True or False. Default is False. Specifies whether all subdirectories of path should be included

FilePathField.allow_files
Optional. Either True or False. Default is True. Specifies whether files in the specified location should be included. Either this or allow_folders must be True.

FilePathField.allow_folders
Optional. Either True or False. Default is False. Specifies whether folders in the specified location should be included. Either this or allow_files must be True.

Of course, these arguments can be used together.

有一点需要提醒的是 match只匹配基本文件名(base filename), 而不是整个文件路径(full path). So, this example:

FilePathField(path="_home_images", match="foo.*", recursive=True)
...will match _home_images_foo.png but not /home_images_foo_bar.png because the match applies to the base filename (foo.png and bar.png).

FilePathField instances are created in your database as varchar columns with a default max length of 100 characters. As with other fields, you can change the maximum length using the max_length argument.

FloatField

class FloatField([options])
用Python的一个float 实例来表示一个浮点数.

该字段的默认组件是一个 TextInput.

FloatField vs. DecimalField

有时候FloatField 类会和DecimalField 类发生混淆. 虽然它们表示都表示实数,但是二者表示数字的方式不一样。FloatField 使用的是Python内部的 float 类型, 而DecimalField 使用的是Python的 Decimal 类型. 想要了解更多二者的差别, 可以查看Python文档中的 decimal 模块.

ImageField

class ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, options])

继承了 FileField的所有属性和方法, 但还对上传的对象进行校验,确保它是个有效的image.

除了从FileField继承来的属性外,ImageField 还有宽和 高属性。

为了更便捷的去用那些属性值, ImageField 有两个额外的可选参数

ImageField.height_field¶
该属性的设定会在模型实例保存时,自动填充图片的高度.

ImageField.width_field¶
该属性的设定会在模型实例保存时,自动填充图片的宽度.

ImageField字段需要调用Pillow 库.

ImageField会创建在你的数据库中 和 varchar 一样,默认最大长度为100和其他字段一样, 你可以使用max_length 参数来设置默认文件最大值.

The default form widget for this field is a ClearableFileInput.

IntegerField

class IntegerField([options])

一个整数。在Django所支持的所有数据库中,从 -2147483648 到 2147483647 范围内的值是合法的。默认的表单输入工具是TextInput.

GenericIPAddressField

class GenericIPAddressField([protocol=both, unpack_ipv4=False, options])¶

和 IPv4 or IPv6 地址, in string format (e.g. 192.0.2.30 or 2a02:42fe::4). 这个字段的默认表单小部件是一个TextInput.

GenericIPAddressField.protocol
Limits valid inputs to the specified protocol. Accepted values are 'both' (default), 'IPv4' or 'IPv6'. Matching is case insensitive.

GenericIPAddressField.unpack_ipv4
Unpacks IPv4 mapped addresses like ::ffff:192.0.2.1. If this option is enabled that address would be unpacked to 192.0.2.1. Default is disabled. Can only be used when protocol is set to 'both'.

If you allow for blank values, you have to allow for null values since blank values are stored as null.

NullBooleanField

class NullBooleanField([options])
Like a BooleanField, but allows NULL as one of the options. Use this instead of a BooleanField with null=True. The default form widget for this field is a NullBooleanSelect.

PositiveIntegerField(正整数字段)

class PositiveIntegerField([options])¶
类似 IntegerField, 但值必须是正数或者零(0). Values from 0 to 2147483647 are safe in all databases supported by Django. The value 0 is accepted for backward compatibility reasons.

PositiveSmallIntegerField¶

class PositiveSmallIntegerField([options])¶
该模型字段类似 PositiveIntegerField, 但是只允许小于某一特定值(依据数据库类型而定)。从0 到 32767 这个区间,对于Django所支持的所有数据库而言都是安全的。

SlugField

class SlugField([max_length=50, options])¶
Slug 是一个新闻术语(通常叫做短标题)。一个slug只能包含字母、数字、下划线或者是连字符,通常用来作为短标签。通常它们是用来放在URL里的。

与CharField类似, 你可以指定 max_length 的值(read the note about database portability and max_length in that section, too). 如果没有指定 max_length, Django将会默认长度为50。

Implies setting Field.db_index to True.

It is often useful to automatically prepopulate a SlugField based on the value of some other value. You can do this automatically in the admin using prepopulated_fields.

SmallIntegerField

class SmallIntegerField([options])

Like an IntegerField, but only allows values under a certain (database-dependent) point. Values from -32768 to 32767 are safe in all databases supported by Django.

TextField

class TextField([options])

巨大的文本域.该模型默认的表单组件是Textarea。

如果在TextField指定max_length属性,将影响到的是Textarea插件,但是不会强制模型或者数据库级别。如果需要限制的话最好使用CharField

TimeField

class TimeField([auto_now=False, auto_now_add=False, options])¶
时间字段,和Python中 datetime.time 一样。Accepts the same auto-population options as DateField.

表单默认为 TextInput.输入框。The admin adds some JavaScript shortcuts.

URLField

class URLField([max_length=200, options])

一个CharField 类型的URL

The default form widget for this field is a TextInput.

Like all CharField subclasses, URLField takes the optional max_length argument. If you don’t specify max_length, a default of 200 is used.

UUIDField

New in Django 1.8.
class UUIDField([options])¶
一个用来存储UUID的字段。使用Python的UUID类。 当使用PostgreSQL数据库时,该字段类型对应的数据库中的数据类型是uuid,使用其他数据库时,数据库对应的是char(32)类型。

使用UUID类型相对于使用具有primary_key参数的AutoField类型是一个更好的解决方案。 数据库不会自动生成UUID,所以推荐使用default参数:

import uuid
from django.db import models

class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

other fields

注意:这里使用了一个方法(省略了括号),而不是使用一个UUID的实例给default参数赋值。

2016/6/8 posted in  python

django 缓存

django 缓存框架

动态网站的基本原因就是每次请求都是动态的,有人在后台添加数据也会同时有人在前台读取数据,web的工作就是每次将数据层查询的数据渲染到前台给客户查看。

其实这里的缓存和CPU系统级别的缓存比较相似,对计算机架构比较清楚的会了解,CPU在读取内存的速度是大于读取磁盘的,而同时CPU进行内存交换的速度同样也有一定的延时,所以CPU引入了CPU缓存机制,针对20-80原则,这个缓存不需要很大,但是缓存的速度基本等同于CPU速度,所以可以提升整体性能。

网站缓存同样也是起到这个作用,数据库当前是存在磁盘上的,所以每次web去取值需要去到磁盘上取,这个性能由数据库性能和磁盘IO来决定。同样的根据80-20原则,大多数的请求实际上是相同的,所以我们可以将内容缓存到内存中,如果某请求在内存中已存在,就不需要重复的再去数据库查询。对于某些更新频率不高的网站,可能memcache可以解决基本所有的查询了。

django 中的伪代码描述

    given a URL, try finding that page in the cache
    if the page is in the cache:
        return the cached page
    else:
        generate the page
        save the generated page in the cache (for next time)
        return the generated page

Django提供了不同级别的缓存粒度:你可以缓存特定视图的输出、你可以仅仅缓存那些很难生产出来的部分、或者你可以缓存你的整个网站。

Django也能很好的配合那些“下游”缓存, 比如 Squid 和基于浏览器的缓存。

设置缓存位置

缓存同样可以存在不同的地方,可以存在数据库中,起到减少数据连接的效用。可以存在文件系统中,减少数据库的负担。也可以存在内存中,提高整体性能。不同的位置对于性能的区别是完全不同的。

Memchache

最高效的缓存类型, Memcached 是一个全部基于内存的缓存服务,起初是为了解决LiveJournal.com负载来开发的,后来是由Danga开源出来的。 它被类似Facebook 和 维基百科这种网站使用,用来减少数据库访问,显著的提高了网站的性能。

Memcached 是个守护进程,它被分配了单独的内存块。 它做的所有工作就是为缓存提供一个快速的添加,检索,删除的接口。 所有的数据直接存储在内存中,所以它不能取代数据库或者文件系统的使用。

需要在Django中使用Memcached时:

将 BACKEND 设置为django.core.cache.backends.memcached.MemcachedCache 或者 django.core.cache.backends.memcached.PyLibMCCache (取决于你所选绑定memcached的方式)
将 LOCATION 设置为 `ip:port 值,ip 是 Memcached 守护进程的ip地址, port 是Memcached 运行的端口。或者设置为 unix:path 值,path 是 Memcached Unix socket file的路径.

memcache的几种绑定模式,本地端口绑定:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }

socket绑定:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }

多服务缓存共享:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11212',
                '172.19.26.244:11213',
            ]
        }
    }

Database caching 数据库级缓存

将缓存保存在数据库中。

数据库缓存配置

为了把数据表用来当做你的缓存后台:

把BACKEND设置为django.core.cache.backends.db.DatabaseCache
把 LOCATION 设置为 tablename, 数据表的名称。这个名字可以是任何你想要的名字,只要它是一个合法的表名并且在你的数据库中没有被使用过。

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
            'LOCATION': 'my_cache_table',
        }
    }   
创建缓存表

在使用之前需要使用django来创建缓存表:

    python manage.py createcachetable

表名会从LOCATION中获取

多数据库缓存

如果你在多数据库的情况下使用数据库缓存,你还必须为你的数据库缓存表设置路由说明。

    class CacheRouter(object):
        """A router to control all database cache operations"""
        def db_for_read(self, model, **hints):
            "All cache read operations go to the replica"
            if model._meta.app_label == 'django_cache':
                return 'cache_replica'
            return None
        def db_for_write(self, model, **hints):
            "All cache write operations go to primary"
            if model._meta.app_label == 'django_cache':
                return 'cache_primary'
            return None
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            "Only install the cache model on primary"
            if app_label == 'django_cache':
                return db == 'cache_primary'
            return None

如果你使用多数据库缓存, createcachetable会在每个缓存中创建一个表。

如果你使用多数据库,createcachetable会遵循你的数据库路由中的allow_migrate()方法

Filesystem caching 文件级缓存

为了使用文件缓存,需要配置两个参数:

把BACKEND配置成"django.core.cache.backends.filebased.FileBasedCache"
把LOCATION配置成相应的文件目录

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache',
        }
    }

如果是windown的话,需要加上盘符

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': 'c:/foo/bar',
        }
    }

Local-memory caching

如果没有设置其他的缓存方式,这种方式将会是setting.py中默认的缓存方式。这种缓存是每进程的,并且是线程安全的。

如果需要使用需要将BACKEND设置成 "django.core.cache.backends.locmem.LocMemCache"

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'unique-snowflake',
        }
    }

如果有多个缓存系统的话,需要将LOCATION的名称区分出来。

Dummy caching

django 的 dummy caching实际上不是一个真实的缓存,他主要实现缓存的interface,但是不进行任何实际操作。

他的用处在于如果生产环境有一个叫重量的缓存,但是本地开发环境不希望缓存,但是你又不希望为这种情况修改代码,就可以使用dummy caching

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}

缓存配置参数

TIMEOUT: 缓存过期时间,以秒为准,默认为300秒

OPTIONS: 由第三方库所支持的缓存将会把这些选项直接配置到底层缓存库

  • MAX_ENTRIES:高速缓存允许的最大条目数,超出这个数则旧值将被删除. 这个参数默认是300.
  • CULL_FREQUENCY:当达到MAX_ENTRIES 的时候,被删除的条目比率。 实际比率是 1 / CULL_FREQUENCY, 所以设置CULL_FREQUENCY 为2会删去一半的缓存MAX_ENTRIES 达到时。这个参数应该是整数,默认为 3. 把 CULL_FREQUENCY的值设置为 0 意味着当达到MAX_ENTRIES时,缓存将被清空。某些缓存后端 (database尤其)这将以很多缓存丢失为代价,大大much 提高接受访问的速度。

KEY_PREFIX:将会自动保存到django服务器的所有缓存key中

VERSION:默认成本的而cache key版本。

KEY_FUNCTION:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache',
            'TIMEOUT': 60,
            'OPTIONS': {
                'MAX_ENTRIES': 1000
            }
        }
    }

使用站点级缓存

一旦高速缓存设置,最简单的方法是使用缓存缓存整个网站。

    MIDDLEWARE_CLASSES = (
        'django.middleware.cache.UpdateCacheMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.cache.FetchFromCacheMiddleware',
    )

'update'中间件,必须放在列表的开始位置,而fectch中间件,必须放在最后。

然后,添加下面这些需要的参数到settings文件里:

CACHE_MIDDLEWARE_ALIAS – 用于存储的缓存的别名

CACHE_MIDDLEWARE_SECONDS –每个page需要被缓存多少秒.

CACHE_MIDDLEWARE_KEY_PREFIX – 如果缓存被多个使用相同Django安装的网站所共享,那么把这个值设成当前网站名,或其他能代表这个Django实例的唯一字符串,以避免key发生冲突。 如果你不在意的话可以设成空字符串。

FetchFromCacheMiddleware 缓存GET和HEAD状态为200的回应,用不同的参数请求相同的url被视为独立的页面,缓存是分开的。

单个view缓存

更加轻巧的缓存方式,可以精确到每个view中。django为每个view定义了相关的装饰器

django.views.decorators.cache 定义了一个自动缓存视图响应的 cache_page装饰器

    from django.views.decorators.cache import cache_page
    @cache_page(60 * 15)
    def my_view(request):
        ...

60*15代表缓存的秒数。

和站点缓存一样,视图缓存与 URL 无关。如果多个 URL 指向同一视图,每个URL将会分别缓存。

    urlpatterns = [
        url(r'^foo/([0-9]{1,2})/$', my_view),
    ]

此案例中发送到_foo/1和 /foo/23会被分别缓存。但是一旦一个明确的 URL (e.g., /foo/23_) 已经被请求过了, 之后再度发出的指向该 URL 的请求将使用缓存

一些额外的参数

cache, 指示修饰符去具体使用缓存 (from your CACHES setting) 当要缓存页面结果时。

    @cache_page(60 * 15, cache="special_cache")
    def my_view(request):
        ...

key_prefix

    @cache_page(60 * 15, key_prefix="site1")
    def my_view(request):
        ...

在URL中使用cache

前面可以看到我们在view中添加了相关的装饰器来使用缓存,这样会导致view层和缓存强耦合。会导致在某些无缓存的站点中重用该视图的时候报错。

解决方法是将cache配置到url中:

from django.views.decorators.cache import cache_page

urlpatterns = [
url(r'foo_([0-9]{1,2})_$', cache_page(60 * 15)(my_view)),
]

模板片段缓存

可以使用cache模板标签来缓存模板的一个片段。使用 {% load cache %} 来指定缓存模板标签

标签{% cache %}将按给定的时间缓存包含块中的内容。

    {% load cache %}
    {% cache 500 sidebar %}
        .. sidebar ..
    {% endcache %}

缓存API

有时候,我们并不希望总是缓存完整的页面,这样实际上有时候效率比较低下

如果网页包含一个view,这个view依赖于几个比较复杂的查询,查询结果在不同的情况下都会变化。在这种情况的时候,使用整页caching结果肯定是不理想的。

在这种时候,django提供了更底层的缓存API,用这样的API能够更好并且更安全的cache数据。

可以参考:

    >>> from django.core.cache import caches
    >>> cache1 = caches['myalias']
    >>> cache2 = caches['myalias']
    >>> cache1 is cache2
    True
    >>> from django.core.cache import get_cache
    >>> get_cache('default')
    >>> get_cache('django.core.cache.backends.memcached.MemcachedCache', LOCATION='127.0.0.2')
    >>> get_cache('default', TIMEOUT=300)

基本的使用方法:

    >>> cache.set('my_key', 'hello, world!', 30)
    >>> cache.get('my_key')
    'hello, world!'

Cache key prefixing

如果你在多台服务器之间,或者在生产和开发环境之间共享cache实例,有可能出现一个数据在一台服务器上缓存,但是需要被另外一台使用。如果缓存数据在两台服务器上是不同的,将导致很多的严重问题。

为了解决这些问题,Django可以提供将可以给不同的服务器添加一个cache前缀,当出现这种情况的时候,django将自动为这些cache添加前缀。

Cache versioning

当运行时的缓存值状态改变的时候,我们需要清除并更新已经存在的cache值。最简单的方法当然是更新整个的缓存区,但是这肯定会导致很多依然可用的缓存丢失。

django为单独的缓存值提供了一个更好的方法,django缓存框架有一个系统记得版本控制器,使用VERSION标签来指定不同缓存。

    # Increment the version of 'my_key'
    >>> cache.incr_version('my_key')
    # The default version still isn't available
    >>> cache.get('my_key')
    None
    # Version 2 isn't available, either
    >>> cache.get('my_key', version=2)
    None
    # But version 3 *is* available
    >>> cache.get('my_key', version=3)
    'hello world!'
2016/6/7 posted in  python

django feed rss

RSS的基本原理

RSS是在互联网上被广泛采用的内容包装和投递协议。网络用户可以在客户端借助于支持RSS的新闻工具软件,在不打开网站内容页面的情况下,阅读支持RSS输出的网站内容。

    <?xml version="1.0" encoding="gb2312" ?> 
    <rss version="2.0"> 
    <channel> 
      <title>我的Blog</title>                 //channel的标题
      <description>与我自己的技术Blog相关联</description>   //channel的介绍
      <link>http://counter.csdn.net/pv.aspx?id=72</link>     //channel的url
      <item> 
      <title><!-- 项标题 --></title>           //item的标题
      <link><!-- 项 URL --></link>           //item的url
      <description><!-- 简要描述 --></description>        //item的介绍
      <!-- 可选的/可扩展的元素 -->        //item的其他属性,比如更新时间
      </item> 
      <item>
      <!-- 可多个<item>项目-->           //一个channel有多个item
      </item>
    </channel>
    </rss>

可以看到这个是RSS的基本定义,可以看到这里面定义的前三行为 title、description、link ,
后面是每一个item定义项。

有的网站提供了RSS自动发现机制,可以很方便地把RSS的URL添加到RSS阅读器中。如果没有自动发现,那么可以手动把RSS链接的URL添加到RSS阅读器中,这样就加入了一个用户订阅的频道。在RSS阅读器中可以更新频道列表或点击一个item链接打开该item的页面。

RSS工作机制

内容提供者在其网站上添加RSS的链接,以提供RSS订阅功能,当打开这个链接时,传送过去了一些频道信息,比如:blog的作者名。

一种做法是,RSS链接URL指向的是一个空内容的页面,该页面后台程序通过传过来的频道信息访问数据库,获取频道列表,用Response.Write向该空页面写出XML格式的文件。

另一种做法是,RSS链接URL指向的是一个xml文件,该文件由服务器的程序事先生成好的,放在服务器上,访问时静态获取,服务器在作者每添加一个频道列表时自动更新该xml文件。

第一种做法的优点是管理方便,因为不需要为每个频道生成xml文件,所有的RSS请求都由一个后台页面处理,接口统一,但每次访问RSS链接时,都要动态地写出RSS频道列表,访问效率相对较低,第二种做法的优点是访问时,只是返回一个静态的xml文件,不需要访问数据库来临时生成,所以访问效率相对较高,但每更新一次频道列表中的项时,就要自动地重新生成xml文件以保证RSS文件的最新,这样就降低了更新的效率。本系统中采用的是第一种方法。

django下面的实现

django自带了feed类来实现RSS,使用自带的feed非常简单,直接使用一个新的类,就可以了

    from django.contrib.syndication.views import Feed
    from django.core.urlresolvers import reverse
    from policebeat.models import NewsItem      
    class LatestEntriesFeed(Feed):
        title = "Police beat site news"
        link = "/sitenews/"
        description = "Updates on changes and additions to police beat central."        
        def items(self):
            return NewsItem.objects.order_by('-pub_date')[:5]       
        def item_title(self, item):
            return item.title       
        def item_description(self, item):
            return item.description     
        # item_link is only needed if NewsItem has no get_absolute_url method.
        def item_link(self, item):
            return reverse('news-item', args=[item.pk])     

title, link 和 description 分别对应 RSS的 , 和 。

其中在items中可以取得所有的需要的对象,在item_title里面返回相关的标题,item_description里面返回详细内容,item_link返回拼装后的链接。

如果你要创建一个Atom feed,而不是RSS feed,你需要使用subtitle属性替代description。查看Publishing Atom and RSS feeds in tandem 里面的例子

然后在url中进行定义

    from django.conf.urls import url
    from myproject.feeds import LatestEntriesFeed       
    urlpatterns = [
        # ...
        url(r'^latest/feed/$', LatestEntriesFeed()),
        # ...
    ]
2016/6/6 posted in  python

django 文件详解

django 文件详解

在python django中处理文件,可以从前台request上传,Model层,view层几个方面来进行解析。

django 文件模型

在模型中定义文件比较简单,只需要定义一个FileField就可以了,其中FileField主要定义了两个比较重要的参数。

FileField.upload_to : 一个本地文件系统的路径,它将附加到MEDIA_ROOT 设置的后面来确定url 属性的值。

FileField.storage: 用于文件的存储

这个定义比较简单,定义好了文件模型之后,在admin视图里面就可以直接上传使用了。他的存放方式一般采取数据库存放upload_to的相关参数,然后将文件存储到MEDIA_ROOT定义的文件目录中。

django 文件前台

在HTML中定义django需求的文件需要将File放置到form中,必须是POST请求,同时需要请求的类型为拥有enctype="multipart/form-data" 属性时,才会包含数据。否则request.FILES 为空。

定义方式可以参考:

    <form id="handfile" action="{% url 'blog:handlefile' %}" method="post" enctype="multipart/form-data">
          <fieldset>
             {% csrf_token %}
              <label for="name" id="name_label">blogfile</label>
              <input type="file" name="blogfile" id="blogfile" />
              <br />
              <input type="submit" name="submit" class="button" id="submit_btn" value="Send Message"/>
          </fieldset>

django view层

在view层中可以获取前台传来的request,request会将前台的多个文件传到File文件中去,需要进行key的匹配,详细的直接上代码

    def handlefile(request):
        for key in request.FILES:
            blog = request.FILES[key]
            print(blog._name)
            #print(blog.read())
            print("read trunk")
            for chunk in blog.chuncks():
                print(chunk)

这里是模型的读取

讲讲几个读取方法 read() readline() readlines() chuncks()
read()方法

.read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。然而 .read() 生成文件内容最直接的字符串表示,但对于连续的面向行的处理,它却是不必要的,并且如果文件大于可用内存,则不可能实现这种处理。

readlines()

.readlines() 同样是一次读取整个文件,象 .read()一样。.readlines() 自动将文件内容分析成一个行的列表,该列表可以由 Python 的 for ... in ... 结构进行处理。

readline()

readline() 每次只读取一行,通常比 .readlines() 慢得多。仅当没有足够内存可以一次读取整个文件时,才应该使用 .readline()。

chuncks()

按照区块来读取,如果是从request直接出来的,使用chuncks会报错,AttributeError: 'InMemoryUploadedFile' object has no attribute 'chuncks'

猜想由于文件已经在内存中了,这是不能在用chuncks()来分块读取了。

2016/6/4 posted in  python

阿里云安装pip镜像配置

阿里云安装pip

在阿里云上使用默认的pip源的时候,经常出现下载很慢,或者有时候出现根本无法下载的情况。只有加载本地的源才行。

pip镜像列表

pip的镜像可以在这里查看 https://pypi-mirrors.org/

可以看到其中已经有了aliyun的镜像,赶紧点进去看看,发现https的是打不开的,只有选择http的。

http://mirrors.aliyun.com/pypi/simple/

访问这个可以进去看看

这里也推荐可以使用pypi.douban.com 豆瓣的或者 其他的。我的直接在阿里云上所以就直接选择阿里云的镜像了,实测也是最快的,下载速度可以到几M

配置pip配置文件

在root目录下创建文件 ~/.pip/pip.conf

详细的配置如下:

    [global]
    timeout = 6000
    index-url = http://mirrors.aliyun.com/pypi/simple
    [install]
    use-mirrors=true
    mirrors= http://mirrors.aliyun.com
    trusted-host=mirrors.aliyun.com

安装pip包

2016/6/1 posted in  python

django 富文本RSS防御

富文本攻击

在富文本编辑器中,可以写入一些相关的html等,在我们读出到页面的时候,由于为了省去html标签,所以一般会使用safe过滤器进行过滤。攻击者可以在富文本编辑器中输入 等方式来进行富文本的攻击。

基本方法

一般可以采用removetags的方式来进行去掉某些关键标签,这在简单的情况下是适用的,如下:

    {% if rtf_con %}
        {{ rtf_con | removetags:"script " | safe }}
    {% endif %}

但是这种方式只能过滤掉某些标签,过滤情况不完全,如大小写之类的便无法过滤。而html又是不标准的,所以很多情况下是过滤不完全的。

使用python-xss-filter防护

富文本XSS基本上使用白名单进行过滤,通过白名单的仅留下需要使用、并且基本不产生危害的标签和属性。

https://github.com/phith0n/python-xss-filter

直接使用python-xss-filter进行富文本的过滤。

基本文本的XSS防御

Django默认开启了输入转义来防范XSS,可以使用两种方法来关闭自动转义

方法一:使用过滤器“|safe”关闭单个变量的自动转义

    <h1>Submit-Content</h1>
    {{ xss_content | safe}}

方法二:使用{%autoescape off%}的方式关闭代码段的自动转义

    {% autoescape off %}
        <h1>Submit-Content</h1>
        {{ xss_content }}
    {% endautoescape %}

在autoescape off中可以局部开启autoescape,使用{% autoescape on%}即可

建议在非必要的情况下,不要关闭Django的自动转义功能。

代码检查:视图模版中是否使用了{{date|safe}}、{%autoescape off%}…{%endautoescape%}

2016/5/31 posted in  python

django 文件目录穿越防护

文件目录穿越

在有input框需要输入文件目录时,由于客户可以输入 ../**.py类似的方式,可以查看到其他路径的文件,从而导致安全风险。

设置文件白名单的方式解决

    def check_allowed_filepath(file_path):
        allowed_filepath = ['F:/app/', 'D:/Python27/Lib/site-packages/django/']
        tag = False
        for tmp in allowed_filepath:
            if cmp(file_path[0:len(tmp)], tmp) == 0:
                tag = True
                break
        return tag

可以将某些文件输入到白名单中,然后在里面进行对比即可。

对于没有前端不允许输入绝对路径的,在进行路径的拼接时不要使用os.path.join,此时如果前端输入正确的绝对路径,则后面的路径将会成为拼接结果,建议使用字符串直接拼接。

在进行代码审查的时候,需要先行确认对服务器上文件进行操作的接口,并确认文件的路径或文件名是否来自于post或get参数。如果存在这样的参数,则需要检查参数是否根据需要进行相应的过滤和处理。

2016/5/31 posted in  python

django iframe 点击劫持防护

点击劫持防护

点击劫持

首先使用iframe加载外部需要劫持的网页,然后通过在外部网页的某些部分上增加一个透明的iframe来劫持某些按钮。当用户点击这个透明iframe区域的时候,跳转到自己的流程控制上。

点击劫持解决方法

微软提出来使用X-FRAME-OPTIONS 来判定加载iframe的方式:

    X-FRAME-OPTIONS的三种配置:
    DENY               // 拒绝任何域加载
    SAMEORIGIN         // 允许同源域下加载
    ALLOW-FROM         // 可以定义允许frame加载的页面地址

django在新建项目的时候默认开启了点击劫持防护

查看settings.pyMIDDLEWARE_CLASSED中已经加载了django.middleware.clickjacking.XFrameOptionsMiddleware

默认情况下 X-FRAME-OPTIONS被设置成为了 SAMEORIGIN

如果需要修改这个值,主需要在setting.py中添加X-FRAME-OPTIONS=ALLOW-FROM这样的就可以了。

一般的解决方式也不是把默认的允许所有打开,为了方便有些推广网站之类的需要加载我们的网站,可能需要开启某些页面允许iframe,把某些关键的信息相关的页面进行保护起来即可,所以我们可以采用下面的一般处理方式

单独添加例外方式

通过增加装饰器的方法来实现,可以使用单独的装饰器来表述相关的

    from django.views.decorators.clickjacking import xframe_options_exempt
    from django.views.decorators.clickjacking import xframe_options_deny
    from django.views.decorators.clickjacking import xframe_options_sameorigin
    @xframe_options_exempt
    @xframe_options_sameorigin
    @xframe_options_deny        

在进行代码审计时,对于没有例外要求的站点,只需要查看settings.py文件中是否开启了'django.middleware.clickjacking.XFrameOptionsMiddleware',并查找确保没有使用@xframe_options_exempt即可。

对于有例外要求的站点,需要合理筛选出例外的url,并找到对应的视图函数,查看其是否根据要求禁用或启用X-FRAME-OPTIONS

2016/5/31 posted in  python

django SQL注入安全整理

SQL注入安全

所有的web形式的操作都需要注意sql注入安全事项。

Django字段查询

    try:
        room_id_get = request.GET.get('Room_ID')
        result = SqlTest.objects.get(room_id=room_id_get)
    except SqlTest.DoesNotExist:
             …………
    try:
        room_id_get = request.GET.get('Room_ID')
        result = SqlTest.objects.filter(room_id=room_id_get)
    except SqlTest.DoesNotExist:
             …………

在这两种通过字段直接get或者filter过滤的时候,将参数直接放入方法中,方法会为我们进行SQL注入过滤防护。

Django raw SQL查询

    try:
        room_id_get = request.GET.get('Room_ID')
        result = SqlTest.objects.raw('select * from test_sql_injection_sqltest where room_id = %s ', [room_id_get] )
    except SqlTest.DoesNotExist:
        …………

使用这种查询方式的时候,要注意不能直接将参数写入raw中,需要以参数的形式封装,程序才会处理防止SQL注入,如果直接将字段放入where中,会引起SQL注入

直接调用底层数据库API

    try:
        room_id_get = request.GET.get('Room_ID')
        cursor = connection.cursor()
        cursor.execute('select * from test_sql_injection_sqltest where room_id = ?', [room_id_get])
        result = cursor.fetchall()
    except:
             …………

使用这种方式的时候,需要注意同样要以参数的形式进行封装,而不要直接放入where中,这样既可以完成防止SQL注入。

总结

在进行Django的代码审计时,重点查找通过表单、URL或ajax传递过来的参数在作为查询条件(或后续有可能成为查询条件)的代码中是否存在objects.raw、objects.extra和cursor.execute函数的应用,并查看此类函数的应用是否根据规范使用。

2016/5/31 posted in  python

django 1.6版本POST请求整理

django 发送POST请求

django发送post请求,如果直接发送,会出现csrf cookie not set 报错以及403错误。网上各种解决方法也比较多。

最靠谱的方法是引入csrf_exempt装饰器,如下既能解决。

    from django.views.decorators.csrf import csrf_exempt
    @csrf_exempt
    def XXX(req):
        return ……

为什么要使用csrf_exempt装饰器

默认情况下,csrf的中间件是通过该方式被全局开启的,对所有的POST生效。可以在看默认的配置

    MIDDLEWARE_CLASSES = [
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.locale.LocaleMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'pagination.middleware.PaginationMiddleware',
    ]

这里的django.middleware.csrf.CsrfViewMiddleware定义了csrf的中间件。

定义这个中间件的目的是整理POST请求的模拟安全,我们需要在post请求体中带上token,防止post请求的仿冒。

那么我们可以采用这种csrf_exempt的方式来进行,但是这种方式是不安全的,因为会去掉了token机制,在服务端不进行token的验证,我们使用下面的方法。

在form中带上{% csrf_token %}

配置如下所示:

    <form id="contact" action="{% url 'blog:sendmail' %}">
      <fieldset>
         {% csrf_token %}
          <span class="error" id="name_error">Please enter name !</span>
          <span class="error" id="email_error">Please enter email address !</span>
          <span class="error" id="email_error2">Please enter valid email address !</span>
          <span class="error" id="msg_error">Please enter message !</span>
          <label for="name" id="name_label">Name (required)</label>
          <input type="text" name="name" id="name" size="50" value="" class="text-input" />
          <label for="email" id="email_label">Email (required)</label>
          <input type="text" name="email" id="email" size="50" value="" class="text-input" />
          <label for="subject" id="subject_label">Subject</label>
          <input type="text" name="subject" id="subject" size="50"  value="" class="text-input" />
          <label for="msg" id="msg_label">Message</label>
          <textarea rows="10" name="msg" id="msg" class="text-input"></textarea>
          <br />
          <input type="submit" name="submit" class="button" id="submit_btn" value="Send Message"/>
      </fieldset>
    </form>

在form中带上csrf_token后会在其中生成一个token。

可以查看源文件看出,其中是这样解析的

<input type="hidden" name="csrfmiddlewaretoken" value="g8GAWhzYB3HGZPwHiVGEtoAkagVu6EtW">

我们可以看到这个带上了相应的token,放在隐藏的iuput标签中,所以如果是form表单直接使用的话,可以加上这个标签就可以了,如果使用ajax发送的话,需要在data里面加上相应数据。

var dataString = 'name='+ name + '&email=' + email + '&subject=' + subject + '&msg=' + msg + '&csrfmiddlewaretoken=' + csrfmiddlewaretoken +;
    //alert (dataString);return false;
    
  jQuery.ajax({
  type: "POST",
  url: "sendmail",
  data: dataString,
  success: function() {
    jQuery('#contactform').html("<div id='message' class='ten columns'></div>");
    jQuery('#message').html("<strong>Contact Form Submitted!</strong>")
    .append("<p>We will be in touch soon.</p>")
    .hide()
    .fadeIn(1500, function() {
      jQuery('#message');
    });
  }
 });

此时去掉csrf_exempt装饰器也可以请求通过了。

在生成表单的页面中生成csrf

在生成表单中的view中生成csrf:

    c = csrf(request)       
    result_dict = {}        
    result_dict.update(c)       
    return render(request, 'csrf/index.html', result_dict)

对于POST请求,可以在目标view方法中对于post请求方法进行限制,否则可能出现服务器内部错误

在view中限制post请求:

    from django.views.decorators.http import require_http_methods
    @require_http_methods(["POST", ])

django的CsrfViewMiddleware原理

Django的实现并不是基于后端的,Django不会在服务端记录会话和对应的token值,而是在初次设置token的时候同时设置一个名为csrftoken的永久Cookie,并且Cookie的值与表单中csrfmiddlewaretoke的值相同。

提交之后,Django通过Cookie和提交的数据进行判断。

具有该Cookie之后,Django将不会每次请求都生成token,而是根据Cookie直接向前端返回token

在csrf中间件全局开启的情况下,对于使用POST方法打开的页面,并且提交的内容没有重要性、没有机密性的可以通过csrf_exempt来关闭

在views中导入csrf_exempt装饰器

    from django.views.decorators.csrf import csrf_exempt

在函数前使用

    @csrf_exempt

对于csrf中间件全局关闭的情况下,对少量页面开启csrf防护可以通过csrf_protect来开启

在views中导入csrf_protect装饰器

    from django.views.decorators.csrf import csrf_protect

在函数前使用

    @csrf_protect

可以自行检测csrf

通过检测referer实现

    referrer = request.META.get('HTTP_REFERER')

通过将referrer与业务流程上正常的referer进行比较,处理非正常referer的请求

    @require_http_methods(['POST', ])
    @csrf_exempt
    def refer_view(request):
    referrer = request.META.get('HTTP_REFERER')
    mail = request.POST.get('mail')
    if referrer != "http://127.0.0.1:8000/test_csrf/" or mail is None or mail == '':
        return HttpResponseRedirect("/test_csrf/")
    return render(request, 'csrf/refer.html', {'result': mail})

Ajax中的csrf

AJAX中的CSRF与普通的使用差别不大,但是有两种实现方式

一种就是利用Django的CSRF实现的原理,利用前端脚本从Cookie中读取csrftoken的值并放在data中提交。

另一种就是在template中直接写入token,直接从template定义data中的tokenmiddlewaretoken字段。

    //方式一:运行getCookie函数,从Cookie中读取token值
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    input_data = "mail=" + $("#mail_input").val() + "&csrfmiddlewaretoken=" + csrftoken;
    //方式二:在模版中写入csrf_token
    
    input_data = "mail=" + $("#mail_input").val() + "&csrfmiddlewaretoken=" + "{{ csrf_token }}";
    //ajax在使用的时候与平常相同
    $.ajax({
        url:'/test_csrf/ajax_api/',
        data:input_data,
        type:'post',
        dateType:'text',
        success:function(data){
            $("#ajax_return").val(data);
        },
        error: function () {
            alert("error");
        }
    })

在进行代码审查的时候,需要审查的settings.py中是否添加了'django.middleware.csrf.CsrfViewMiddleware',并且涉及POST请求的view中是否没有使用@csrf_exempt

另外,如果传输的数据非常重要,建议增加其他校验方式如手机验证码、图片验证码或其他第三方因素进行校验。

2016/5/30 posted in  python

django 发送邮件

django 发送邮件

django集成了发送邮件模块,主要集成在django.core.mail中。下面来整理一下django发送邮件的相关步骤。

1. django配置setting

在setting中可以配置相关初始指定函数

    EMAIL_HOST = 'smtp.163.com'                   
    EMAIL_PORT = 25                                 
    EMAIL_HOST_USER = 'melonblogs@163.com'
    EMAIL_HOST_PASSWORD = ********
    EMAIL_SUBJECT_PREFIX = u'[melonblogs]'
    EMAIL_USE_TLS = True        
    SERVER_EMAIL = 'melonblogs@163.com'

2. 配置发送邮件

这里例子通过前台获取相关的资料,然后进行邮件发送:

    @csrf_exempt
    def sendmail(request):
        name = request.POST.get('name', 'customer')
        email = request.POST.get('email', 'melonblogs@163.com')
        subject = request.POST.get('subject')
        subject = 'name:' + name + '\n from email:' + email + '\n subject:' + subject
        msg = request.POST.get('msg')
        send_mail(subject, msg, 'melonblogs@163.com', ['59170121@qq.com'],fail_silently=False)
        return render(request, 'contact.html')

调用这个代码后就可以发送邮件了

2016/5/30 posted in  python

django ImageField 解析

Django ImageField

Django 中的模型层可以直接使用ImageField来指定图片应用。

django 使用ImageFile来控制上传的图片文件,但是这种文件需要安装Pillow。使用pip install Pillow就可以了。

ubuntu 下安装 Pillow

在我的阿里云上面执行的时候发现Pillow安装有比较多的依赖包,否则安装会报错,具体的可以使用

    $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
    $ sudo apt-get build-dep python-imaging
    $ sudo apt-get install libjpeg8 libjpeg62-dev libfreetype6 libfreetype6-dev
    $ pip install Pillow

官方文档中这么说明

class ImageField([upload_to=None, height_field=None, width_field=None, max_length=100, options])¶

继承了 FileField的所有属性和方法, 但还对上传的对象进行校验,确保它是个有效的image.

除了从FileField继承来的属性外,ImageField 还有宽和 高属性。

为了更便捷的去用那些属性值, ImageField 有两个额外的可选参数

ImageField.height_field¶

该属性的设定会在模型实例保存时,自动填充图片的高度.

ImageField.width_field¶

该属性的设定会在模型实例保存时,自动填充图片的宽度.

ImageField字段需要调用Pillow 库.

ImageField会创建在你的数据库中 和 varchar 一样,默认最大长度为100和其他字段一样, 你可以使用max_length 参数来设置默认文件最大值.

The default form widget for this field is a ClearableFileInput.

使用中说明

在使用中可以看到ImageField有两个参数,height_fieldwidth_field。 这两个参数的作用是将上传的图片的长和宽保存到这个参数定义的字段中。所以这两个参数接受的是字符串类型的字段,而不要输入其他的类型或者变量。

案例如下:

模型层定义:

widthfield = models.IntegerField('widthfiled', default=400)
heightfield= models.IntegerField('heightfield', default=400)
head_image = models.ImageField(upload_to='bookmarks/%Y/%m',width_field='widthfield', height_field='heightfield', null=True, blank=True)

图片前台整理

在setting中设置:

MEDIA_ROOT = BASE_DIR + '/blog/media'
MEDIA_URL = '/media/'

设置相应的url:

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^mshow/', include('mshow.urls', namespace='mshow')),
url(r'^blog/', include('blog.urls', namespace='blog')),
url(r'^comments/', include('django_comments.urls')),
url(r'^media/(?P<path>.*)$', django.views.static.serve,{'document_root': settings.MEDIA_ROOT}) 
]

设置相应的url, 绑定到相应的图片目录

2016/5/28 posted in  python

django pagedown web 编辑器

django markdown web 编辑器大概的介绍

几个编辑工具

简书,一个在线轻博客网站,我正在用的。
Mou OSX 下面的markdown编辑器。支持主题模式及部分的代码模式
csdn csdn最近也开始支持markdown的编辑器了。
stackio,在线编辑器,双页面的模式,非常不错,做的非常不错,如果做博客编辑后续可以专门配置。
markable, 在线编辑器,不太适用
dillinger,和stackio类似,可以直接使用。

js插件式

epiceditor, 这个用的人比较多,直接用js就可以集成,也可以直接看。没有扩展按钮,更加适用于专业的IT人事。
MarkdownEditing,比较专注的markdown编辑器,暂时还没有时间研究
remark,同样的也是插件维护
pagedown-bootstrap,使用bootstrap维护的markdown 编辑器,适用于手机
markdown-js

django下的插件形式

django-pagedown, 当前选择使用的markdown编辑器,可以直接在django里面使用,比较方便。
django-markdownx,同样支持两版展示,觉得没有django-pagedown展示效果好,后续再做具体实验。

django-pagedown

安装

  1. 使用pip 安装 django-pagedown: pip install django-pagedown
  2. 在 setting.py 文件的 installed_app文件里面添加pagedown

配置

全局替换模式

直接在全局里面配置,在某一个面板中配置这个的话会覆盖这个面板里面的所有TextField的默认widget。

这种模式最直接也最直观的进行了修改

from pagedown.widgets import AdminPagedownWidget
from django.db import models    
class FooModelAdmin(models.ModelAdmin):
    formfield_overrides = {
        models.TextField: {'widget': AdminPagedownWidget },
    }
其他模式

其他详细模式可以参考项目的git 上面的参考文档 django-pagedown

django-markdown-deux解析markdown

在django 1.6版本就不支持django-markdown包了,这个包很好的完善了这个功能。

安装

使用 sudo pip install django-markdown-deux来安装即可

有的地方要提示安装markdown2,但是在安装完成之后就可以看到用pip实际把这个包就已经安装进来了

Successfully installed django-markdown-deux-1.0.5 markdown2-2.3.1

当然不要忘记吧markdown-deux装入app中。

直接使用

如果确定Text中只有markdown标签,不进行html标签解析的话,可以直接加入通道标签就可以使用

在模板中添加标签load

{% load markdown_deux_tags %}

在页面中加通道渲染

{{ content|markdown }}

自定义markdown filter

新建一个包文件夹templatetags,然后在里面添加__init__.pydjangomarkdown.py 。这个djangomarkdown模板是以后模板过滤器使用的。也不要和现在的模板过滤器冲突了。

myproject/
myapp/
    __init__.py
    models.py
    templatetags/
        __init__.py
        djangomarkdown.py
    views.py

djangomarkdown.py

# -*- coding: utf-8 -*-
import markdown2
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(is_safe=True)
@stringfilter
def djangomarkdown(value):
    return mark_safe(markdown2.markdown(force_unicode(value),
                                        extras=["code-friendly"]
                                        )
                     )

在extras可以填入具体的参量,用于使用markdown的扩展语法。

完成后就可以同时过滤HTML标签和markdown标签了。

markdown-deux 的一些介绍

详情可以参考django-markdown-deux github页面。

2016/5/24 posted in  python

django 标签

2016/5/19 posted in  python