python之路-Django进阶

建立表结构css

 1 from django.db import models
 2 
 3 # Create your models here.
 4 class Book(models.Model):
 5     name = models.CharField(max_length=128)
 6     price = models.PositiveSmallIntegerField(null=True)
 7     authors = models.ManyToManyField('Author')
 8     publisher = models.ForeignKey('Publisher')
 9     pub_date = models.DateField()
10     #memo = models.CharField(null=True,max_length=64)
11 
12     def __str__(self):
13         return self.name                 #返回字符串
14 
15 class Author(models.Model):
16     name = models.CharField(max_length=32)
17     email = models.EmailField(unique=True)
18     def __str__(self):
19         return self.name
20 
21 class Publisher(models.Model):
22     name = models.CharField(max_length=128,unique=True)
23     website = models.URLField(unique=True)
24 
25     def __str__(self):
26         return '%s %s' %(self.name,self.website)
表结构
  1 AutoField(Field)
  2         - int自增列,必须填入参数 primary_key=True
  3 
  4     BigAutoField(AutoField)
  5         - bigint自增列,必须填入参数 primary_key=True
  6 
  7         注:当model中若是没有自增列,则自动会建立一个列名为id的列
  8         from django.db import models
  9 
 10         class UserInfo(models.Model):
 11             # 自动建立一个列名为id的且为自增的整数列
 12             username = models.CharField(max_length=32)
 13 
 14         class Group(models.Model):
 15             # 自定义自增列
 16             nid = models.AutoField(primary_key=True)
 17             name = models.CharField(max_length=32)
 18 
 19     SmallIntegerField(IntegerField):
 20         - 小整数 -32768 ~ 32767
 21 
 22     PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
 23         - 正小整数 0 ~ 32767
 24     IntegerField(Field)
 25         - 整数列(有符号的) -2147483648 ~ 2147483647
 26 
 27     PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
 28         - 正整数 0 ~ 2147483647
 29 
 30     BigIntegerField(IntegerField):
 31         - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
 32 
 33     自定义无符号整数字段
 34 
 35         class UnsignedIntegerField(models.IntegerField):
 36             def db_type(self, connection):
 37                 return 'integer UNSIGNED'
 38 
 39         PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
 40             'AutoField': 'integer AUTO_INCREMENT',
 41             'BigAutoField': 'bigint AUTO_INCREMENT',
 42             'BinaryField': 'longblob',
 43             'BooleanField': 'bool',
 44             'CharField': 'varchar(%(max_length)s)',
 45             'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
 46             'DateField': 'date',
 47             'DateTimeField': 'datetime',
 48             'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
 49             'DurationField': 'bigint',
 50             'FileField': 'varchar(%(max_length)s)',
 51             'FilePathField': 'varchar(%(max_length)s)',
 52             'FloatField': 'double precision',
 53             'IntegerField': 'integer',
 54             'BigIntegerField': 'bigint',
 55             'IPAddressField': 'char(15)',
 56             'GenericIPAddressField': 'char(39)',
 57             'NullBooleanField': 'bool',
 58             'OneToOneField': 'integer',
 59             'PositiveIntegerField': 'integer UNSIGNED',
 60             'PositiveSmallIntegerField': 'smallint UNSIGNED',
 61             'SlugField': 'varchar(%(max_length)s)',
 62             'SmallIntegerField': 'smallint',
 63             'TextField': 'longtext',
 64             'TimeField': 'time',
 65             'UUIDField': 'char(32)',
 66 
 67     BooleanField(Field)
 68         - 布尔值类型
 69 
 70     NullBooleanField(Field):
 71         - 能够为空的布尔值
 72 
 73     CharField(Field)
 74         - 字符类型
 75         - 必须提供max_length参数, max_length表示字符长度
 76 
 77     TextField(Field)
 78         - 文本类型
 79 
 80     EmailField(CharField):
 81         - 字符串类型,Django Admin以及ModelForm中提供验证机制
 82 
 83     IPAddressField(Field)
 84         - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
 85 
 86     GenericIPAddressField(Field)
 87         - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
 88         - 参数:
 89             protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
 90             unpack_ipv4, 若是指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,须要protocol="both"
 91 
 92     URLField(CharField)
 93         - 字符串类型,Django Admin以及ModelForm中提供验证 URL
 94 
 95     SlugField(CharField)
 96         - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、链接符(减号)
 97 
 98     CommaSeparatedIntegerField(CharField)
 99         - 字符串类型,格式必须为逗号分割的数字
100 
101     UUIDField(Field)
102         - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
103 
104     FilePathField(Field)
105         - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
106         - 参数:
107                 path,                      文件夹路径
108                 match=None,                正则匹配
109                 recursive=False,           递归下面的文件夹
110                 allow_files=True,          容许文件
111                 allow_folders=False,       容许文件夹
112 
113     FileField(Field)
114         - 字符串,路径保存在数据库,文件上传到指定目录
115         - 参数:
116             upload_to = ""      上传文件的保存路径
117             storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
118 
119     ImageField(FileField)
120         - 字符串,路径保存在数据库,文件上传到指定目录
121         - 参数:
122             upload_to = ""      上传文件的保存路径
123             storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
124             width_field=None,   上传图片的高度保存的数据库字段名(字符串)
125             height_field=None   上传图片的宽度保存的数据库字段名(字符串)
126 
127     DateTimeField(DateField)
128         - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
129 
130     DateField(DateTimeCheckMixin, Field)
131         - 日期格式      YYYY-MM-DD
132 
133     TimeField(DateTimeCheckMixin, Field)
134         - 时间格式      HH:MM[:ss[.uuuuuu]]
135 
136     DurationField(Field)
137         - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
138 
139     FloatField(Field)
140         - 浮点型
141 
142     DecimalField(Field)
143         - 10进制小数
144         - 参数:
145             max_digits,小数总长度
146             decimal_places,小数位长度
147 
148     BinaryField(Field)
149         - 二进制类型
150 
151 字段
字段
  1 ForeignKey(ForeignObject) # ForeignObject(RelatedField)
  2         to,                         # 要进行关联的表名
  3         to_field=None,              # 要关联的表中的字段名称
  4         on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
  5                                         - models.CASCADE,删除关联数据,与之关联也删除
  6                                         - models.DO_NOTHING,删除关联数据,引起错误IntegrityError
  7                                         - models.PROTECT,删除关联数据,引起错误ProtectedError
  8                                         - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段须要设置为可空)
  9                                         - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段须要设置默认值)
 10                                         - models.SET,删除关联数据,
 11                                                       a. 与之关联的值设置为指定值,设置:models.SET(值)
 12                                                       b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
 13 
 14                                                         def func():
 15                                                             return 10
 16 
 17                                                         class MyModel(models.Model):
 18                                                             user = models.ForeignKey(
 19                                                                 to="User",
 20                                                                 to_field="id"
 21                                                                 on_delete=models.SET(func),)
 22         related_name=None,          # 反向操做时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 23         related_query_name=None,    # 反向操做时,使用的链接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 24         limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
 25                                     # 如:
 26                                             - limit_choices_to={'nid__gt': 5}
 27                                             - limit_choices_to=lambda : {'nid__gt': 5}
 28 
 29                                             from django.db.models import Q
 30                                             - limit_choices_to=Q(nid__gt=10)
 31                                             - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
 32                                             - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 33         db_constraint=True          # 是否在数据库中建立外键约束
 34         parent_link=False           # 在Admin中是否显示关联数据
 35 
 36 
 37     OneToOneField(ForeignKey)
 38         to,                         # 要进行关联的表名
 39         to_field=None               # 要关联的表中的字段名称
 40         on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
 41 
 42                                     ###### 对于一对一 ######
 43                                     # 1. 一对一其实就是 一对多 + 惟一索引
 44                                     # 2.当两个类之间有继承关系时,默认会建立一个一对一字段
 45                                     # 以下会在A表中额外增长一个c_ptr_id列且惟一:
 46                                             class C(models.Model):
 47                                                 nid = models.AutoField(primary_key=True)
 48                                                 part = models.CharField(max_length=12)
 49 
 50                                             class A(C):
 51                                                 id = models.AutoField(primary_key=True)
 52                                                 code = models.CharField(max_length=1)
 53 
 54     ManyToManyField(RelatedField)
 55         to,                         # 要进行关联的表名
 56         related_name=None,          # 反向操做时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 57         related_query_name=None,    # 反向操做时,使用的链接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 58         limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
 59                                     # 如:
 60                                             - limit_choices_to={'nid__gt': 5}
 61                                             - limit_choices_to=lambda : {'nid__gt': 5}
 62 
 63                                             from django.db.models import Q
 64                                             - limit_choices_to=Q(nid__gt=10)
 65                                             - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
 66                                             - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 67         symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否建立反向操做的字段
 68                                     # 作以下操做时,不一样的symmetrical会有不一样的可选字段
 69                                         models.BB.objects.filter(...)
 70 
 71                                         # 可选字段有:code, id, m1
 72                                             class BB(models.Model):
 73 
 74                                             code = models.CharField(max_length=12)
 75                                             m1 = models.ManyToManyField('self',symmetrical=True)
 76 
 77                                         # 可选字段有: bb, code, id, m1
 78                                             class BB(models.Model):
 79 
 80                                             code = models.CharField(max_length=12)
 81                                             m1 = models.ManyToManyField('self',symmetrical=False)
 82 
 83         through=None,               # 自定义第三张表时,使用字段用于指定关系表
 84         through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段作多对多关系表
 85                                         from django.db import models
 86 
 87                                         class Person(models.Model):
 88                                             name = models.CharField(max_length=50)
 89 
 90                                         class Group(models.Model):
 91                                             name = models.CharField(max_length=128)
 92                                             members = models.ManyToManyField(
 93                                                 Person,
 94                                                 through='Membership',
 95                                                 through_fields=('group', 'person'),
 96                                             )
 97 
 98                                         class Membership(models.Model):
 99                                             group = models.ForeignKey(Group, on_delete=models.CASCADE)
100                                             person = models.ForeignKey(Person, on_delete=models.CASCADE)
101                                             inviter = models.ForeignKey(
102                                                 Person,
103                                                 on_delete=models.CASCADE,
104                                                 related_name="membership_invites",
105                                             )
106                                             invite_reason = models.CharField(max_length=64)
107         db_constraint=True,         # 是否在数据库中建立外键约束
108         db_table=None,              # 默认建立第三张表时,数据库中表的名称
109 
110 字段以及参数
字段及参数

经过python manage.py shellhtml

from app01 import models前端

增长数据:node

models.Book.objects.create(name='go',price=50,pub_date='2018-09-20',publisher_id=2)python

查询:jquery

models.Book.objects.all()git

查询的结果是列表,能够经过操做列表来取数web

b1 = models.Book.objects.all()[0]shell

b1.name数据库

manytomany的查询方法

b1.authors.all()

建立做者:

 models.Author.objects.create(name='oldboy',email='oldboy@126.com')

将做者添加到书里(前提是必须是建立好的数据,manytomany是不容许建立数据时操做的)

 b4 = models.Book.objects.create(name='kanjian',price=199,pub_date='2016-07-21',publisher_id=2)

b4.authors.add(1,2)

b4.save()

select * from app01_book_authors;

这样在这样表里就会有对应关系

b4.authors.remove(1,2) 删除

forienkey的查询方法

b4.publisher.name     (写上字段名,在加上关联表的字段名)

 1 #
 2     #
 3     # models.Tb1.objects.create(c1='xx', c2='oo')  增长一条数据,能够接受字典类型数据 **kwargs
 4 
 5     # obj = models.Tb1(c1='xx', c2='oo')
 6     # obj.save()
 7 
 8     #
 9     #
10     # models.Tb1.objects.get(id=123)         # 获取单条数据,不存在则报错(不建议)
11     # models.Tb1.objects.all()               # 获取所有
12     # models.Tb1.objects.filter(name='seven') # 获取指定条件的数据
13 
14     #
15     #
16     # models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据
17 
18     #
19     # models.Tb1.objects.filter(name='seven').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
20     # obj = models.Tb1.objects.get(id=1)
21     # obj.c1 = '111'
22     # obj.save()                                                 # 修改单条数据
23 
24 基本操做
基本操做

利用双下划线将字段和对应的操做链接起来

 1 # 获取个数
 2         #
 3         # models.Tb1.objects.filter(name='seven').count()
 4 
 5         # 大于,小于
 6         #
 7         # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
 8         # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
 9         # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
10         # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
11         # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
12 
13         # in
14         #
15         # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于十一、2二、33的数据
16         # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
17 
18         # isnull
19         # Entry.objects.filter(pub_date__isnull=True)
20 
21         # contains
22         #
23         # models.Tb1.objects.filter(name__contains="ven")
24         # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
25         # models.Tb1.objects.exclude(name__icontains="ven")
26 
27         # range
28         #
29         # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
30 
31         # 其余相似
32         #
33         # startswith,istartswith, endswith, iendswith,
34 
35         # order by
36         #
37         # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
38         # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc
39 
40         # group by
41         #
42         # from django.db.models import Count, Min, Max, Sum
43         # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
44         # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
45 
46         # limit 、offset
47         #
48         # models.Tb1.objects.all()[10:20]
49 
50         # regex正则匹配,iregex 不区分大小写
51         #
52         # Entry.objects.get(title__regex=r'^(An?|The) +')
53         # Entry.objects.get(title__iregex=r'^(an?|the) +')
54 
55         # date
56         #
57         # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
58         # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
59 
60         # year
61         #
62         # Entry.objects.filter(pub_date__year=2005)
63         # Entry.objects.filter(pub_date__year__gte=2005)
64 
65         # month
66         #
67         # Entry.objects.filter(pub_date__month=12)
68         # Entry.objects.filter(pub_date__month__gte=6)
69 
70         # day
71         #
72         # Entry.objects.filter(pub_date__day=3)
73         # Entry.objects.filter(pub_date__day__gte=3)
74 
75         # week_day
76         #
77         # Entry.objects.filter(pub_date__week_day=2)
78         # Entry.objects.filter(pub_date__week_day__gte=2)
79 
80         # hour
81         #
82         # Event.objects.filter(timestamp__hour=23)
83         # Event.objects.filter(time__hour=5)
84         # Event.objects.filter(timestamp__hour__gte=12)
85 
86         # minute
87         #
88         # Event.objects.filter(timestamp__minute=29)
89         # Event.objects.filter(time__minute=46)
90         # Event.objects.filter(timestamp__minute__gte=29)
91 
92         # second
93         #
94         # Event.objects.filter(timestamp__second=31)
95         # Event.objects.filter(time__second=2)
96         # Event.objects.filter(timestamp__second__gte=31)
97 
98 进阶操做
View Code

包含: models.Book.objects.filter(name__contains='go') icontains是忽略大小写

 

聚合

求书的平均价格

首先导入模块

from django.db.models import Avg         Max,Min,Sum,Count

 models.Book.objects.all().aggregate(Avg('price'))

统计数量

models.Book.objects.count()

分组聚合

 models.Book.objects.values('publisher__name').annotate(Count('id'))

查看出版社各处了几本书,经过书的id能够查看,由于书的id不一样

models.Book.objects.values('publisher__name').annotate(Avg('price'))

F语句

数值增长、本表中的两个字段比较

from django.db.models import F

models.Book.objects.update(price=F('price')+10)

models.Book.objects.values('price')

本表中的两个字段比较

entry.object.filter(n_content__gt=F('n_pingbacks'))

将一个字段中的值移到另外一个字段 

models.Book.objects.update(memo=F('name'))

 

 

Q语句

至关于or

from django.db.models import Q

 q = Q(pub_date__year=2016) | Q(pub_date__year=2018) 作为条件,将|换作,就表示and

 models.Book.objects.filter(q).values('pub_date')  而后查询

注意

若是在已经建立好的表中增长字段时,必须附一个默认值,这样才不会报错

1.memo = models.CharField(null=True,max_length=64)     null=True是必需要写的,

2.python manage.py makemigrations

 python manage.py migrate

 

Form表单

 以发邮件为例:

先后端都须要验证,前端验证目的是减少后端的验证压力

经过后端来实现,而后传给后端

1 urlpatterns = [
2     url(r'^admin/', admin.site.urls),
3     url(r'^mail$',views.mail)
4 ]
url
 1 from django.shortcuts import render,HttpResponse
 2 from app01.forms import MailSendForm
 3 # Create your views here.
 4 def mail(request):
 5     if request.method == 'POST':
 6         form = MailSendForm(request.POST)   #接受前端发来的数据
 7         if form.is_valid():                 #验证表单数据是否合法
 8             print('going to send mail....',form.cleaned_data)   #清洗数据,后端要求数据,若是前端发过来的数据是字符串,就会尝试变成数字
 9             return HttpResponse('ok')
10         else:
11             print('error happend during validation',form.errors)
12     else:
13         form = MailSendForm()
14         return render(request,'mail_send.html',{'form':form})
views

须要咱们自行建立一个前端页面,经过这个页面可实现基本验证,并且也不用再html中也具体的元素,而且要在views中导入

 1 from django.shortcuts import render,HttpResponse
 2 from app01.forms import MailSendForm
 3 # Create your views here.
 4 def mail(request):
 5     if request.method == 'POST':
 6         form = MailSendForm(request.POST)   #接受前端发来的数据
 7         if form.is_valid():                 #验证表单数据是否合法
 8             print('going to send mail....',form.cleaned_data)   #清洗数据,后端要求数据,若是前端发过来的数据是字符串,就会尝试变成数字
 9             return HttpResponse('ok')
10         else:
11             print('error happend during validation',form.errors)
12     else:
13         form = MailSendForm()
14         return render(request,'mail_send.html',{'form':form})
forms
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <form method="POST">
 9         {{ form.as_p }}             
10         <input type="submit" value="提交">
11     </form>
12 
13 </body>
14 </html>
html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <form method="POST">
 9         {% for item in form %}
10             <div>
11             {{ item.label }} {{ item }}
12             {% if item.errors %}
13                 <span style="color: red">
14                 {{ item.errors }}
15                 </span>
16             {% endif %}
17             </div>
18         {% endfor %}
19         <input type="submit" value="提交">
20     </form>
21 
22 </body>
23 </html>
html扩展

forms中德widget相似于小工具,有一些具体的功能。

对一些字段作认证

clean_sender,对那些字段作认证,就写那个字段,此例中对sender作认证

html中的一些as_的功能,可如今html中的一些情景,例如as_p,就是p标签

form其余的事例

 1 from django import forms
 2 import re
 3 from app01 import models
 4 FAVORITE_COLORS_CHOICES = (
 5     ('blue','Blue'),
 6     ('green','Green'),
 7     ('black','Bloack'),
 8 )
 9 BIRTH_YEAR_CHOICE = ('1980','1981','1982')
10 
11 
12 def mobile_validate(value):
13         mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
14         if not mobile_re.match(str(value)):
15             raise forms.ValidationError('手机号码格式错误')
16 class MailSendForm(forms.Form):
17     sender = forms.EmailField()
18     receiver = forms.EmailField()
19     subject = forms.CharField(max_length=12)
20     content = forms.CharField(widget=forms.Textarea(attrs={'cols':100,
21                                                            'class':'font-color',
22                                                            'style':'background-color:lightgreen'
23                                                            }))
24     birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICE))   #此处的years表示是能选择的项目
25     seat_CHOICES = (('1','First',),('2','Second',))
26    
27     choice_field = forms.ChoiceField(widget=forms.RadioSelect,choices=seat_CHOICES)     #choice 表示选择的范围
28     favorite_colors = forms.MultipleChoiceField(
29         widget=forms.CheckboxSelectMultiple,
30         choices=FAVORITE_COLORS_CHOICES,
31     )
32 
33     def clean_sender(self):
34         print('validate sender',self.cleaned_data)
35         if self.cleaned_data.get('sender') != 'alex@126.com':
36             self.add_error('sender','Only alex has right to send email')
37 
38         return self.cleaned_data.get('sender')     #此处是为了验证后获得干净的数据,业务逻辑用
form扩展

 对多个字段进行验证

 

1     def clean(self):
2         print('clean data:::',self.cleaned_data)
3         sender = self.cleaned_data.get('sender')
4         received = self.cleaned_data.get('received')
5 
6         if sender == received:
7             raise forms.ValidationError('发送者和接收者不能相同')     #raise全局错误,就不用errors
View Code

 

对手机号进行验证
validator验证插件

 1 from django import forms
 2 import re
 3 from app01 import models
 4 FAVORITE_COLORS_CHOICES = (
 5     ('blue','Blue'),
 6     ('green','Green'),
 7     ('black','Bloack'),
 8 )
 9 BIRTH_YEAR_CHOICE = ('1980','1981','1982')
10 
11 
12 def mobile_validate(value):
13         mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')    #经过compile作个模版,而后经过模版匹配。
14         if not mobile_re.match(str(value)):                                                    #re只能匹配字符串
15             raise forms.ValidationError('手机号码格式错误')
16 class MailSendForm(forms.Form):
17     sender = forms.EmailField()
18     receiver = forms.EmailField()
19     subject = forms.CharField(max_length=12)
20     content = forms.CharField(widget=forms.Textarea(attrs={'cols':100,
21                                                            'class':'font-color',
22                                                            'style':'background-color:lightgreen'
23                                                            }))
24     birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICE))   #此处的years表示是能选择的项目
25     seat_CHOICES = (('1','First',),('2','Second',))
26     mobile = forms.IntegerField(validators=[mobile_validate,],
27                                 error_messages={'required':'手机不能为空'},
28                                 widget=forms.TextInput(attrs={'class':'from-control',
29                                                               'placeholder':'手机号码'}))
30     choice_field = forms.ChoiceField(widget=forms.RadioSelect,choices=seat_CHOICES)     #choice 表示选择的范围
31     favorite_colors = forms.MultipleChoiceField(
32         widget=forms.CheckboxSelectMultiple,
33         choices=FAVORITE_COLORS_CHOICES,
34     )
35 
36     def clean_sender(self):
37         print('validate sender',self.cleaned_data)
38         if self.cleaned_data.get('sender') != 'alex@126.com':
39             self.add_error('sender','Only alex has right to send email')
40 
41         return self.cleaned_data.get('sender')     #此处是为了验证后获得干净的数据,业务逻辑用
42 
43     def clean(self):
44         print('clean data:::',self.cleaned_data)
45         sender = self.cleaned_data.get('sender')
46         received = self.cleaned_data.get('received')
47 
48         if sender == received:
49             raise forms.ValidationError('发送者和接收者不能相同')     #raise全局错误,就不用errors
50 
51 
52 
53 class BookForm(forms.ModelForm):
54     class Meta:
55         model = models.Book
56         exclude = ['memo2']
validator

默认只的设置,能够设置多个,用逗号隔开

 1 def mail(request):
 2     if request.method == 'POST':
 3         form = MailSendForm(request.POST)
 4         if form.is_valid(): #验证表单数据是否合法
 5             print('going to send email to .....',form.cleaned_data)
 6         else:
 7             print('error happend during validation:',form.errors)
 8     else:
 9         form = MailSendForm(initial={'sender':'sb_alex@126.com','content':'text mail'})    #默认值
10 
11     return render(request,'mail_send.html',{'form':form})
views中 initial

form.has_changed():判断表单数据是否被改过。通常和默认值配合使用

修改数据,直接建立一个form,与数据库中的表关联起来。(modelform)

1 from appo1 import models
2 class BookForm(forms.ModelForm):
3     class Meta:
4         model = models.Book
5         #fields = "__all__"
6         #fields = ['name','price','authors','pub_date','publisher']
7         exclude = ['memo2',] 
forms
 1 from app01 import models
 2 def book_mgr(request):
 3 
 4     if request.method == "POST":
 5         form = BookForm(data=request.POST)
 6         if form.is_valid():             #添加一条数据
 7             form.save()
 8             form = BookForm()
 9 
10     else:
11         form = BookForm()
12     return render(request,'book.html',{'form':form})
View Code

 

视图的定义

function based view 只支持post,get (函数)

Class based view


经过出版社反向查询出了多少本书
p1 = models.Publisher.objects.last()
dir(p1)
p1.book_set.all() 就能够反向查出出了多少本书book是表名。只有外键能够反向查询

 

admin
建立用户

from bbs import models

admin.site.register(models.Article) 若是须要admin管理的话,就须要注册表

python manage.py createsuperuser

migration文件夹中有每次操做数据库的内容。每次操做都有记录。

 

user = models.ForienKey(User,null=True,default=None)  在表中建立字段,就会出现多个用户对应一个djang用户

 

 user = models.OneToOneField(User,null=True,default=None) 此种方法建立的用户就是与django一一对应

静态文件补充

STATIC_URL = '/static/'     静态文件入口,只要写static,就会默认到STATICFILES_DIRS下找须要的内容。实现前端和后端彻底隔离。找到就会返回。若是有重名,第二个就不会返回了。
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'statics'),
os.path.join(BASE_DIR,'static_files'),
)

 

论坛

 1 from django.db import models
 2 from django.contrib.auth.models import User
 3 
 4 # Create your models here.
 5 
 6 
 7 
 8 class UserProifle(models.Model):
 9     user = models.OneToOneField(User,null=True,default=None)     #此处要关联django的User表
10     name = models.CharField(max_length=32)
11 
12     def __str__(self):
13         return self.name
14 
15 class Article(models.Model):
16     """文章表"""
17     title = models.CharField(max_length=128,unique=True)
18     author = models.ForeignKey("UserProifle")
19     category = models.ForeignKey("Category")
20     pub_date = models.DateTimeField(auto_now_add=True,auto_created=True)
21     tags = models.ManyToManyField("Tag", null=True)
22     body = models.TextField(max_length=100000)
23     head_img = models.ImageField(upload_to="uploads")
24     status_choices = ((0,'草稿'),(1,'发布'),(2,'隐藏'))
25     priority = models.SmallIntegerField(default=1000,verbose_name="优先级")
26 
27     def __str__(self):
28         return self.title
29 
30 class Category(models.Model):
31     """板块"""
32     name = models.CharField(max_length=64,unique=True)
33     set_as_top_menu = models.BooleanField(default=True)
34 
35     def __str__(self):
36         return self.name
37 
38 
39 class Tag(models.Model):
40     """标签表"""
41     name = models.CharField(max_length=64, unique=True)
42     def __str__(self):
43         return self.name
44 
45 class Comment(models.Model):
46     """评论"""
47     article = models.ForeignKey("Article")
48     p_node = models.ForeignKey("Comment",null=True,blank=True,related_name="my_child_comments")
49 
50     user = models.ForeignKey("UserProifle")
51     date = models.DateTimeField(auto_now_add=True)
52     comment = models.TextField(max_length=1024)
53 
54 
55     def __str__(self):
56         return self.comment
57 
58 class Like(models.Model):
59     """点赞"""
60     article = models.ForeignKey("Article")
61     user = models.ForeignKey("UserProifle")
62     date = models.DateTimeField(auto_now_add=True)
63 
64 
65 class PrivateMail(models.Model):
66     """私信"""
67     pass
models

若是想让django管理表,就必须在admin.py中注册

 1 from django.contrib import admin
 2 from app01 import models
 3 # Register your models here.
 4 admin.site.register(models.Article)
 5 admin.site.register(models.UserProifle)
 6 admin.site.register(models.Tag)
 7 admin.site.register(models.Comment)
 8 admin.site.register(models.Like)
 9 admin.site.register(models.PrivateMail)
10 admin.site.register(models.Category)
admin

BootStrap的简单应用

1.导入bootstrap 将bootstrap导入到statics目录下

2.将想要引用的模版另存为,选项中选所有,保存。

3.将html文件保存到到templates 而后将模版中的路径作修改,修改为/static/js    或者其余。

若是路径中涉及到的文件没有的话,再从保存的模版中复制到相应的statics目录下的文件中。(js、css)

注意:经过concole能够查看导入中遇到的错误。

 

论坛的页面呈现

首先咱们要显示模块的展现

若是想要模块的展现,首先咱们须要将其中数据库中拿出来,并返回给页面

提取数据:

views:

1 def index(request):
2     categories =models.Category.objects.filter(set_as_top_menu=True)
3 
4     return render(request,'index.html',{'caterogies':categories})

在html网页中循环列出

1  {% for category in categories %}
2 
3                 <li class="active"><a href="#">{{ category.name }}</a></li>
4             {% endfor %}

实现点击单个模块,而后高亮显示,并将每一个模块的内容显示(想当于切换一个新页面),这就须要在url中写入地址

url

1 url(r'^category/(\d+)/$', views.category,name="category" ),
2 #这个name是别名,要引用
1  <ul class="nav navbar-nav">
2             {% for category in categories %}
3 
4                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
5             {% endfor %}
6           </ul>

能够经过id来实现。

1 def category(request,category_id):
2     categories = models.Category.objects.filter(set_as_top_menu=True)
3 
4     articles = models.Article.objects.filter(category_id=category_id)
5 
6     return render(request,'index.html', {"categories":categories, "articles":articles})

在前段显示

 1  <div class="container">
 2 
 3       <div class="starter-template">
 4         <h1>Bootstrap starter template</h1>
 5         <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
 6         {{ articles }}
 7 
 8           <h2>{{ request.path }}</h2>
 9 
10       </div>
11 
12     </div><!-- /.container -->
前端显示

经过request.path获取url路径,而后经过这个路径在前端找到,并使其活跃

    <script>

        $(document).ready(function () {
            $(".navbar-nav a[href='{{ request.path }}']").parent().addClass("active");
        });//end doc ready

    </script>

显示内容

相似于抽屉的排版

页面布局:

整个布局分12份,左边内容占8个栅格

1   <div class="row">
2             <div class="col-lg-8">
3                 left content
4             </div>
5             <div class="col-lg-4">
6                 right content
7             </div>
8         </div>

左边内容每一个文章都包含内容和图片

{% for article in articles %}
                    <div class="row">
                        <div class="col-lg-10">
                            <h3>{{ article.title }}</h3>
                        </div>
                        <div class="col-lg-2">
                            <img src="{{ article.head_img }}" >
                        </div>
                    </div>
                {% endfor %}

点赞图片的引入

在base.html导入<link href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.css" rel="stylesheet">

而后找到具体图标的html格式,写入html中

1 <div>
2          <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
3  </div>

显示图片(默认会传到uploads目录),此时静态文件是没法找到的,把upload加到static中

经过自定义标签实现,去除uploads

在项目里穿件templatetags,在里面建立一个文件bbs_tags

1 from django.template import Library
2 register = Library()
3 @register.simple_tag
4 def truncate_upload_img(img_src):
5     return img_src.name.lstrip('/uploads')

在html中

{%load bbs_tags%}

在img标签中写

 <img src="\static\{% truncate_upload_img article.head_img %}" >

点赞、评论图标

 1 <div class="row">
 2                                 <div class="col-md-2">
 3                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
 4                                 </div>
 5                                 <div class="col-md-2">
 6                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
 7                                 </div>
 8                                 <hr style="border: 1px dashed darkgray">
 9 
10                             </div>

文章分割线

 1  <div class="row">
 2                         <div class="col-lg-10">
 3                             <h3>{{ article.title }}</h3>
 4                             <div class="row">
 5                                 <div class="col-md-2">
 6                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
 7                                 </div>
 8                                 <div class="col-md-2">
 9                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
10                                 </div>
11                                 <hr style="border: 1px dashed darkgray">
12 
13                             </div>
14                         </div>

分页处理
django已经提供了分页功能( Paginator)

 1 >>> from django.core.paginator import Paginator
 2 >>> objects = ['john', 'paul', 'george', 'ringo']
 3 >>> p = Paginator(objects, 2)
 4 
 5 >>> p.count
 6 4
 7 >>> p.num_pages
 8 2
 9 >>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
10 <class 'range_iterator'>
11 >>> p.page_range
12 range(1, 3)
13 
14 >>> page1 = p.page(1)
15 >>> page1
16 <Page 1 of 2>
17 >>> page1.object_list
18 ['john', 'paul']
19 
20 >>> page2 = p.page(2)
21 >>> page2.object_list
22 ['george', 'ringo']
23 >>> page2.has_next()
24 False
25 >>> page2.has_previous()
26 True
27 >>> page2.has_other_pages()
28 True
29 >>> page2.next_page_number()
30 Traceback (most recent call last):
31 ...
32 EmptyPage: That page contains no results
33 >>> page2.previous_page_number()
34 1
35 >>> page2.start_index() # The 1-based index of the first item on this page
36 3
37 >>> page2.end_index() # The 1-based index of the last item on this page
38 4
39 
40 >>> p.page(0)
41 Traceback (most recent call last):
42 ...
43 EmptyPage: That page number is less than 1
44 >>> p.page(3)
45 Traceback (most recent call last):
46 ...
47 EmptyPage: That page contains no results
方法
 1 {% for contact in contacts %}
 2     {# Each "contact" is a Contact model object. #}
 3     {{ contact.full_name|upper }}<br />
 4     ...
 5 {% endfor %}
 6 
 7 <div class="pagination">
 8     <span class="step-links">
 9         {% if contacts.has_previous %}
10             <a href="?page={{ contacts.previous_page_number }}">previous</a>
11         {% endif %}
12 
13         <span class="current">
14             Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
15         </span>
16 
17         {% if contacts.has_next %}
18             <a href="?page={{ contacts.next_page_number }}">next</a>
19         {% endif %}
20     </span>
21 </div>
实例

其中p.page_range,对象的一个方法,可是前段页面其实拿到的是
 page1 = p.page(1)因此并无range方法,此时咱们须要经过page1.paginator.page_range实现

注:objects.all()其实并无将数据取回,只有循环的时候才开始取数。

bootstrap有分页图标

页数始终显示固定的数量,以中间页为固定,先后显示三页(循环到页数与当前页作对比,小于等于3显示,其余不显示)

模版中是没有加减运算的,只能经过simple_tag解决

 1 {% extends "base.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block body %}
 6     <nav class="navbar navbar-inverse navbar-fixed-top">
 7       <div class="container" >
 8         <div class="navbar-header">
 9           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
10             <span class="sr-only">Toggle navigation</span>
11             <span class="icon-bar"></span>
12             <span class="icon-bar"></span>
13             <span class="icon-bar"></span>
14           </button>
15           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
16         </div>
17         <div id="navbar" class="collapse navbar-collapse">
18           <ul class="nav navbar-nav">
19             {% for category in categories %}
20 
21                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
22             {% endfor %}
23           </ul>
24         </div><!--/.nav-collapse -->
25       </div>
26     </nav>
27 
28     <div class="container" style="background-color: whitesmoke;" >
29         <div class="row">
30             <div class="col-lg-8">
31                 {% for article in articles %}
32 
33                     <div class="row">
34                         <div class="col-lg-10">
35                             <h3>{{ article.title }}</h3>
36                             <div class="row">
37                                 <div class="col-md-2">
38                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
39                                 </div>
40                                 <div class="col-md-2">
41                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
42                                 </div>
43                                 <hr style="border: 1px dashed darkgray">
44 
45                             </div>
46                         </div>
47                         <div class="col-lg-2">
48                             <img height="50px" width="50px" src="\static\{% truncate_upload_img article.head_img %}" >
49                         </div>
50                     </div>
51                 {% endfor %}
52                 <nav aria-label="...">
53                       <ul class="pagination">
54                         <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
55                             {% for page in articles.paginator.page_range %}
56                                 {% if articles.number == page %}
57                                     <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
58                                 {% else %}
59 {#                                    <li class=""><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>#}
60                                     {% render_paginator_btn articles page %}
61                                 {% endif %}
62                             {% endfor %}
63                       </ul>
64                 </nav>
65                 <p>共{{ articles.paginator.count }}篇文章</p>
66             </div>
67 
68             <div class="col-lg-4">
69                 right content
70             </div>
71         </div>
72     </div><!-- /.container -->
73 
74 {% endblock %}
index
 1 from django.template import Library
 2 from django.utils.safestring import mark_safe
 3 register = Library()
 4 @register.simple_tag
 5 def truncate_upload_img(img_src):
 6     return img_src.name.lstrip('/uploads')
 7 
 8 @register.simple_tag
 9 def render_paginator_btn(articles,page):
10     current_page = articles.number
11     if abs(current_page - page) <=3:
12         ele="""<li><a href="?page={page}">{page}</a></li>""".format(page=page)   #直接生成一个html
13         return mark_safe(ele)
14     return ''    #不知足小于3就返回空格
bbs_tag

点击进入,查看内容
继承index.html,将不须要的内容重写。

 1 {% extends 'index.html' %}
 2 {% block left-panel-content %}
 3     <h1>{{ article.title }}</h1>
 4     <p>{{ article.author }}</p>
 5     <div class="row">
 6         <div class="col-md-4">
 7             {{ article.pub_date }}
 8         </div>
 9         <div class="col-md-2">
10             评论{{ article.comment_set.count }}    {# 反向查询,找出文章关联的评论 #}
11         </div>
12     </div>
13     <div >
14          {{ article.body }}
15     </div>
16 
17 
18 {% endblock %}
article.html
 1 from django.shortcuts import render
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 # Create your views here.
 5 
 6 
 7 def index(request):
 8 
 9 
10     categories = models.Category.objects.filter(set_as_top_menu=True)
11     return render(request,'index.html', {"categories":categories})
12 
13 
14 def category(request,category_id):
15     categories = models.Category.objects.filter(set_as_top_menu=True)
16     articles = models.Article.objects.filter(category_id=category_id)
17     paginator = Paginator(articles, 5)  # Show 3 contacts per page
18 
19     page = request.GET.get('page')
20     try:
21         objs = paginator.page(page)
22     except PageNotAnInteger:
23         # If page is not an integer, deliver first page.
24         objs = paginator.page(1)
25     except EmptyPage:
26         # If page is out of range (e.g. 9999), deliver last page of results.
27         objs = paginator.page(paginator.num_pages)
28 
29 
30     return render(request,'index.html', {"categories":categories, "articles":objs})
31 
32 def article_detail(request,article_id):
33     article_obj = models.Article.objects.get(id=article_id)
34     return render(request,'article.html',{'article':article_obj})
views
 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r'^admin/', admin.site.urls),
23     url(r'^$', views.index ),
24     url(r'^category/(\d+)/$', views.category,name="category" ),
25     url(r'^article/(\d+)/$', views.article_detail,name="article_detail" ),
26 ]
url
 1 {% extends "base.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block body %}
 6     <nav class="navbar navbar-inverse navbar-fixed-top">
 7       <div class="container" >
 8         <div class="navbar-header">
 9           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
10             <span class="sr-only">Toggle navigation</span>
11             <span class="icon-bar"></span>
12             <span class="icon-bar"></span>
13             <span class="icon-bar"></span>
14           </button>
15           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
16         </div>
17         <div id="navbar" class="collapse navbar-collapse">
18           <ul class="nav navbar-nav">
19             {% for category in categories %}
20 
21                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
22             {% endfor %}
23           </ul>
24         </div><!--/.nav-collapse -->
25       </div>
26     </nav>
27 
28     <div class="container" style="background-color: whitesmoke;" >
29         <div class="row">
30             <div class="col-lg-8">
31               {% block left-panel-content %}
32                 {% for article in articles %}
33 
34                     <div class="row">
35                         <div class="col-lg-10">
36                             <h3>{{ article.title }}</h3>
37                             <div class="row">
38                                 <div class="col-md-2">
39                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
40                                 </div>
41                                 <div class="col-md-2">
42                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
43                                 </div>
44                                 <hr style="border: 1px dashed darkgray">
45 
46                             </div>
47                         </div>
48                         <div class="col-lg-2">
49                             <img height="50px" width="50px" src="\static\{% truncate_upload_img article.head_img %}" >
50                         </div>
51                     </div>
52                 {% endfor %}
53                 <nav aria-label="...">
54                       <ul class="pagination">
55                         <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
56                             {% for page in articles.paginator.page_range %}
57                                 {% if articles.number == page %}
58                                     <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
59                                 {% else %}
60 {#                                    <li class=""><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>#}
61                                     {% render_paginator_btn articles page %}
62                                 {% endif %}
63                             {% endfor %}
64                       </ul>
65                 </nav>
66                 <p>共{{ articles.paginator.count }}篇文章</p>
67               {% endblock %}
68             </div>
69 
70             <div class="col-lg-4">
71                 right content
72             </div>
73         </div>
74     </div><!-- /.container -->
75 
76 {% endblock %}
index。html

文章样式设置(富文本编辑器)
使用ckeditor,这个编辑器支持djaogo(http://ckeditor.com/download)

下载模版,将模版发到piugin目录下,

 1 {% extends 'index.html' %}
 2 {% block extra_head_resources %}
 3      <script src="/static/plugin/ckeditor/ckeditor.js"></script>
 4 {% endblock %}
 5 {% block container %}
 6     <div style="height: 600px">
 7         <form>
 8             <textarea name="editor1" id="article_editor" rows="10" cols="80">
 9                 This is my textarea to be replaced with CKEditor.
10             </textarea>
11 
12         </form>
13     </div>
14     <script>
15         // Replace the <textarea id="editor1"> with a CKEditor
16         // instance, using default configuration.
17         CKEDITOR.replace( 'article_editor' );
18     </script>
19 
20 {% endblock %}
new_article.html
 1 <!DOCTYPE html>
 2 <!-- saved from url=(0048)http://v3.bootcss.com/examples/starter-template/ -->
 3 <html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 4 
 5     <meta http-equiv="X-UA-Compatible" content="IE=edge">
 6     <meta name="viewport" content="width=device-width, initial-scale=1">
 7     <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! -->
 8     <meta name="description" content="">
 9     <meta name="author" content="">
10     <link rel="icon" href="http://v3.bootcss.com/favicon.ico">
11 
12     <title>1024</title>
13 
14     <!-- Bootstrap core CSS -->
15     <link href="/static/css/bootstrap.min.css" rel="stylesheet">
16 
17     <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
18     <link href="/static/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
19 
20     <!-- Custom styles for this template -->
21     <link href="/static/css/starter-template.css" rel="stylesheet">
22 
23     <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
24     <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
25     <script src="/static/js/ie-emulation-modes-warning.js"></script>
26     <link href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.css" rel="stylesheet">
27 
28     {% block extra_head_resources %}{% endblock %}
29   </head>
30 
31   <body style="background-color:gainsboro;">
32 
33 {% block body %}{% endblock %}
34     <!-- Bootstrap core JavaScript
35     ================================================== -->
36     <!-- Placed at the end of the document so the pages load faster -->
37     <script src="/static/js/jquery.min.js"></script>
38     <script src="/static/js/bootstrap.min.js"></script>
39     <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
40     <script src="/static/js/ie10-viewport-bug-workaround.js"></script>
41 
42 
43     <script>
44 
45         $(document).ready(function () {
46             $(".navbar-nav a[href='{{ request.path }}']").parent().addClass("active");
47         });//end doc ready
48 
49     </script>
50 
51   {% block bottom-js %}{% endblock %}
52 
53 </body></html>
base.html
 1 from django.shortcuts import render
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 # Create your views here.
 5 
 6 
 7 def index(request):
 8 
 9 
10     categories = models.Category.objects.filter(set_as_top_menu=True)
11     return render(request,'index.html', {"categories":categories})
12 
13 
14 def category(request,category_id):
15     categories = models.Category.objects.filter(set_as_top_menu=True)
16     articles = models.Article.objects.filter(category_id=category_id)
17     paginator = Paginator(articles, 5)  # Show 3 contacts per page
18 
19     page = request.GET.get('page')
20     try:
21         objs = paginator.page(page)
22     except PageNotAnInteger:
23         # If page is not an integer, deliver first page.
24         objs = paginator.page(1)
25     except EmptyPage:
26         # If page is out of range (e.g. 9999), deliver last page of results.
27         objs = paginator.page(paginator.num_pages)
28 
29 
30     return render(request,'index.html', {"categories":categories, "articles":objs})
31 
32 def article_detail(request,article_id):
33     article_obj = models.Article.objects.get(id=article_id)
34     return render(request,'article.html',{'article':article_obj})
35 
36 def new_article(request):
37     return render(request, 'new_article.html')
views
 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r'^admin/', admin.site.urls),
23     url(r'^$', views.index ),
24     url(r'^category/(\d+)/$', views.category,name="category" ),
25     url(r'^article/(\d+)/$', views.article_detail,name="article_detail" ),
26     url(r'^new_article/$', views.new_article, name="new_article"),
27 ]
urls
 1 {% extends "base.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block body %}
 6     <nav class="navbar navbar-inverse navbar-fixed-top">
 7       <div class="container" >
 8         <div class="navbar-header">
 9           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
10             <span class="sr-only">Toggle navigation</span>
11             <span class="icon-bar"></span>
12             <span class="icon-bar"></span>
13             <span class="icon-bar"></span>
14           </button>
15           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
16         </div>
17         <div id="navbar" class="collapse navbar-collapse">
18           <ul class="nav navbar-nav">
19             {% for category in categories %}
20 
21                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
22             {% endfor %}
23           </ul>
24         </div><!--/.nav-collapse -->
25       </div>
26     </nav>
27 
28     <div class="container" style="background-color: whitesmoke;" >
29         {% block container %}
30         <div class="row">
31             <div class="col-lg-8">
32               {% block left-panel-content %}
33                 {% for article in articles %}
34 
35                     <div class="row">
36                         <div class="col-lg-10">
37                             <h3>{{ article.title }}</h3>
38                             <div class="row">
39                                 <div class="col-md-2">
40                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
41                                 </div>
42                                 <div class="col-md-2">
43                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
44                                 </div>
45                                 <hr style="border: 1px dashed darkgray">
46 
47                             </div>
48                         </div>
49                         <div class="col-lg-2">
50                             <img height="50px" width="50px" src="\static\{% truncate_upload_img article.head_img %}" >
51                         </div>
52                     </div>
53                 {% endfor %}
54                 <nav aria-label="...">
55                       <ul class="pagination">
56                         <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
57                             {% for page in articles.paginator.page_range %}
58                                 {% if articles.number == page %}
59                                     <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
60                                 {% else %}
61 {#                                    <li class=""><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>#}
62                                     {% render_paginator_btn articles page %}
63                                 {% endif %}
64                             {% endfor %}
65                       </ul>
66                 </nav>
67                 <p>共{{ articles.paginator.count }}篇文章</p>
68               {% endblock %}
69             </div>
70 
71             <div class="col-lg-4">
72                 right content
73             </div>
74         </div>
75          {% endblock %}
76     </div><!-- /.container -->
77 
78 {% endblock %}
index.html

接下来,经过富文本编辑建立文章 

 1 {% extends "base.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block body %}
 6     <nav class="navbar navbar-inverse navbar-fixed-top">
 7       <div class="container" >
 8         <div class="navbar-header">
 9           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
10             <span class="sr-only">Toggle navigation</span>
11             <span class="icon-bar"></span>
12             <span class="icon-bar"></span>
13             <span class="icon-bar"></span>
14           </button>
15           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
16         </div>
17         <div id="navbar" class="collapse navbar-collapse">
18           <ul class="nav navbar-nav">
19             {% for category in categories %}
20 
21                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
22             {% endfor %}
23           </ul>
24         </div><!--/.nav-collapse -->
25       </div>
26     </nav>
27 
28     <div class="container" style="background-color: whitesmoke;" >
29         <div class="row">
30             <div class="col-lg-8">
31                 {% for article in articles %}
32 
33                     <div class="row">
34                         <div class="col-lg-10">
35                             <h3>{{ article.title }}</h3>
36                             <div class="row">
37                                 <div class="col-md-2">
38                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
39                                 </div>
40                                 <div class="col-md-2">
41                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
42                                 </div>
43                                 <hr style="border: 1px dashed darkgray">
44 
45                             </div>
46                         </div>
47                         <div class="col-lg-2">
48                             <img height="50px" width="50px" src="\static\{% truncate_upload_img article.head_img %}" >
49                         </div>
50                     </div>
51                 {% endfor %}
52                 <nav aria-label="...">
53                       <ul class="pagination">
54                         <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
55                             {% for page in articles.paginator.page_range %}
56                                 {% if articles.number == page %}
57                                     <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
58                                 {% else %}
59                                     <li class=""><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
60                                 {% endif %}
61                             {% endfor %}
62                       </ul>
63                 </nav>
64                 <p>共{{ articles.paginator.count }}篇文章</p>
65             </div>
66 
67             <div class="col-lg-4">
68                 right content
69             </div>
70         </div>
71     </div><!-- /.container -->
72 
73 {% endblock %}
html
 1 from django.shortcuts import render
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 # Create your views here.
 5 
 6 
 7 def index(request):
 8 
 9 
10     categories = models.Category.objects.filter(set_as_top_menu=True)
11     return render(request,'index.html', {"categories":categories})
12 
13 
14 def category(request,category_id):
15     categories = models.Category.objects.filter(set_as_top_menu=True)
16     articles = models.Article.objects.filter(category_id=category_id)
17     paginator = Paginator(articles, 3)  # Show 25 contacts per page
18 
19     page = request.GET.get('page')
20     try:
21         objs = paginator.page(page)
22     except PageNotAnInteger:
23         # If page is not an integer, deliver first page.
24         objs = paginator.page(1)
25     except EmptyPage:
26         # If page is out of range (e.g. 9999), deliver last page of results.
27         objs = paginator.page(paginator.num_pages)
28 
29 
30     return render(request,'index.html', {"categories":categories, "articles":objs})
views

 1.首先先生成一个表单,而后经过表单去建立,这里仍是要用户bootstrap的表单,经过modelform实现获取元素

在生成的过程当中,引用bootstrap时没法引用的样式,因此要经过modelform来添加样式

 1 from django import forms
 2 from bbs import models
 3 class ArticleForm(forms.ModelForm):
 4     def __new__(cls, *args, **kwargs):     #重写new方法,
 5         for field_name in cls.base_fields:    #cls.base_fields获取全部的元素
 6             field = cls.base_fields[field_name]
 7             attr_dic = {'class':'form-control'} #给元素加样式
 8             field.widget.attrs.update(attr_dic)  #更新
 9         return forms.ModelForm.__new__(cls)          #由于上面重写了new方法,此时须要继承父类的new方法,不然就不会生效了
10     class Meta:
11         model = models.Article
12         fields = '__all__'
forms
 1 {% extends 'index.html' %}
 2 {% block extra_head_resources %}
 3      <script src="/static/plugin/ckeditor/ckeditor.js"></script>
 4 {% endblock %}
 5 {% block container %}
 6     <div style="height: 600px">
 7         <form>
 8             <textarea name="editor1" id="article_editor" rows="10" cols="80">
 9                 This is my textarea to be replaced with CKEditor.
10             </textarea>
11             {% for field in form %}
12                 <div class="form-group">
13                     <label class="col-sm-2 control-label">{{ field.name }}</label>
14                     <div class="col-sm-10">
15                         {{ field }}
16                     </div>
17                 </div>
18             {% endfor %}
19 
20         </form>
21     </div>
22     <script>
23         // Replace the <textarea id="editor1"> with a CKEditor
24         // instance, using default configuration.
25         CKEDITOR.replace( 'article_editor' );
26     </script>
27 
28 {% endblock %}
new_article.html
 1 from django.shortcuts import render
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 from bbs import forms
 5 # Create your views here.
 6 
 7 
 8 def index(request):
 9 
10 
11     categories = models.Category.objects.filter(set_as_top_menu=True)
12     return render(request,'index.html', {"categories":categories})
13 
14 
15 def category(request,category_id):
16     categories = models.Category.objects.filter(set_as_top_menu=True)
17     articles = models.Article.objects.filter(category_id=category_id)
18     paginator = Paginator(articles, 5)  # Show 3 contacts per page
19 
20     page = request.GET.get('page')
21     try:
22         objs = paginator.page(page)
23     except PageNotAnInteger:
24         # If page is not an integer, deliver first page.
25         objs = paginator.page(1)
26     except EmptyPage:
27         # If page is out of range (e.g. 9999), deliver last page of results.
28         objs = paginator.page(paginator.num_pages)
29 
30 
31     return render(request,'index.html', {"categories":categories, "articles":objs})
32 
33 def article_detail(request,article_id):
34     article_obj = models.Article.objects.get(id=article_id)
35     return render(request,'article.html',{'article':article_obj})
36 
37 def new_article(request):
38     article_from = forms.ArticleForm()
39     return render(request, 'new_article.html',{'form':article_from})
views

2.接下来咱们须要将富文本编辑器放到body中,这就须要对单个元素作修改
富文本编辑器有一个id,咱们想办法把这个id加入到body中就能够了,经过增长样式,只需修改forms就能够。

 1 from django import forms
 2 from bbs import models
 3 class ArticleForm(forms.ModelForm):
 4     def __new__(cls, *args, **kwargs):     #重写new方法,
 5         for field_name in cls.base_fields:    #cls.base_fields获取全部的元素
 6             field = cls.base_fields[field_name]
 7             attr_dic = {'class':'form-control'} #给元素加样式
 8             if field =='body':                          #经过判断能够直接更新
 9                 attr_dic.update({'id':'article_editor'})
10             field.widget.attrs.update(attr_dic)  #更新
11         return forms.ModelForm.__new__(cls)          #由于上面重写了new方法,此时须要继承父类的new方法,不然就不会生效了
12     class Meta:
13         model = models.Article
14         fields = '__all__'
forms

还有一种方法是,直接将new_article.html修改为 CKEDITOR.replace( 'id_body' );
提交表单

 1 {% extends 'index.html' %}
 2 {% block extra_head_resources %}
 3      <script src="/static/plugin/ckeditor/ckeditor.js"></script>
 4 {% endblock %}
 5 {% block container %}
 6     <div style="height: 600px">
 7         <form method="POST">
 8             {% for field in form %}
 9                 <div class="form-group">
10                     <label class="col-sm-2 control-label">{{ field.name }}</label>
11                     <div class="col-sm-10">
12                         {{ field }}
13                     </div>
14                 </div>
15             {% endfor %}
16             <input type="submit" value="提交" class="col-lg-offset-5 btn btn-sm btn-success">
17         </form>
18     </div>
19     <script>
20         // Replace the <textarea id="editor1"> with a CKEditor
21         // instance, using default configuration.
22         CKEDITOR.replace( 'id_body' );
23     </script>
24 
25 {% endblock %}
new_article.html

 <input type="submit" value="提交" class="col-lg-offset-5 btn btn-sm btn-success"> offset移动,btn设置大小以及颜色

3.提交数据

注意事项:以前咱们把做者和priority隐藏掉,因此在提交时会报错,没有author_id,因此在提交时须要获取author_id,获取方法request.user.

request.POST是一个字典,咱们能够把author_id,追加到其中再提交.

这里有个多对多的问题,只有先建立对象,而后才能添加多对多的关系。

 1 from django.shortcuts import render,HttpResponse
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 from bbs import forms
 5 # Create your views here.
 6 
 7 
 8 def index(request):
 9 
10 
11     categories = models.Category.objects.filter(set_as_top_menu=True)
12     return render(request,'index.html', {"categories":categories})
13 
14 
15 def category(request,category_id):
16     categories = models.Category.objects.filter(set_as_top_menu=True)
17     articles = models.Article.objects.filter(category_id=category_id)
18     paginator = Paginator(articles, 5)  # Show 3 contacts per page
19 
20     page = request.GET.get('page')
21     try:
22         objs = paginator.page(page)
23     except PageNotAnInteger:
24         # If page is not an integer, deliver first page.
25         objs = paginator.page(1)
26     except EmptyPage:
27         # If page is out of range (e.g. 9999), deliver last page of results.
28         objs = paginator.page(paginator.num_pages)
29 
30 
31     return render(request,'index.html', {"categories":categories, "articles":objs})
32 
33 def article_detail(request,article_id):
34     article_obj = models.Article.objects.get(id=article_id)
35     return render(request,'article.html',{'article':article_obj})
36 
37 def new_article(request):
38     if request.method == 'POST':
39         # request.POST['author_id'] =request.user.id 由于forms中已经把author去除了,因此此时加进去也不会处理,直接将数据加入到最后最后验证过的干净数据中
40         article_from = forms.ArticleForm(data=request.POST,files=request.FILES)  #后端传文件是必须使用request.FILES
41         if article_from.is_valid():
42             # article_from.save()
43             article_from.cleaned_data['author_id'] = request.user.id
44             tags = article_from.cleaned_data.pop('tags')
45             obj = models.Article(**article_from.cleaned_data)
46             obj.save()
47             obj.tags.add(*tags)      #此处注意这是一个列表,因此须要*
48             obj.save()
49             return HttpResponse("""<h3><a href='/article/%s'>%s</a></h3>""" %(obj.id,obj.title))
50 
51     else:
52         article_from = forms.ArticleForm()
53     return render(request, 'new_article.html',{'form':article_from})
views

{{ article.body | safe }} safe、marksafe是将html格式所有全换为真正的html文件。

4.返回首页,显示的所有信息

 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r'^admin/', admin.site.urls),
23     url(r'^$', views.index ),
24     url(r'^category/(\d+|all)/$', views.category,name="category" ),
25     url(r'^article/(\d+)/$', views.article_detail,name="article_detail" ),
26     url(r'^new_article/$', views.new_article, name="new_article"),
27 ]
urls
 1 from django.shortcuts import render,HttpResponse
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 from bbs import forms
 5 # Create your views here.
 6 
 7 
 8 def index(request):
 9 
10 
11     categories = models.Category.objects.filter(set_as_top_menu=True)
12     return render(request,'index.html', {"categories":categories})
13 
14 
15 def category(request,category_id):
16     categories = models.Category.objects.filter(set_as_top_menu=True)
17     if category_id == 'all':
18         articles = models.Article.objects.all()
19     else:
20         articles = models.Article.objects.filter(category_id_=category_id)
21     paginator = Paginator(articles, 5)  # Show 3 contacts per page
22 
23     page = request.GET.get('page')
24     try:
25         objs = paginator.page(page)
26     except PageNotAnInteger:
27         # If page is not an integer, deliver first page.
28         objs = paginator.page(1)
29     except EmptyPage:
30         # If page is out of range (e.g. 9999), deliver last page of results.
31         objs = paginator.page(paginator.num_pages)
32 
33 
34     return render(request,'index.html', {"categories":categories, "articles":objs})
35 
36 def article_detail(request,article_id):
37     article_obj = models.Article.objects.get(id=article_id)
38     return render(request,'article.html',{'article':article_obj})
39 
40 def new_article(request):
41     if request.method == 'POST':
42         # request.POST['author_id'] =request.user.id 由于forms中已经把author去除了,因此此时加进去也不会处理,直接将数据加入到最后最后验证过的干净数据中
43         article_from = forms.ArticleForm(data=request.POST,files=request.FILES)  #后端传文件是必须使用request.FILES
44         if article_from.is_valid():
45             # article_from.save()
46             article_from.cleaned_data['author_id'] = request.user.id
47             tags = article_from.cleaned_data.pop('tags')
48             obj = models.Article(**article_from.cleaned_data)
49             obj.save()
50             obj.tags.add(*tags)      #此处注意这是一个列表,因此须要*
51             obj.save()
52             return HttpResponse("""<h3><a href='/article/%s'>%s</a></h3>""" %(obj.id,obj.title))
53 
54     else:
55         article_from = forms.ArticleForm()
56     return render(request, 'new_article.html',{'form':article_from})
views
 1 {% extends "base.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block body %}
 6     <nav class="navbar navbar-inverse navbar-fixed-top">
 7       <div class="container" >
 8         <div class="navbar-header">
 9           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
10             <span class="sr-only">Toggle navigation</span>
11             <span class="icon-bar"></span>
12             <span class="icon-bar"></span>
13             <span class="icon-bar"></span>
14           </button>
15           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
16         </div>
17         <div id="navbar" class="collapse navbar-collapse">
18           <ul class="nav navbar-nav">
19             <li class=""><a href="{% url 'category' 'all' %}">所有</a></li>
20             {% for category in categories %}
21 
22                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
23             {% endfor %}
24           </ul>
25         </div><!--/.nav-collapse -->
26       </div>
27     </nav>
28 
29     <div class="container" style="background-color: whitesmoke;" >
30         {% block container %}
31         <div class="row">
32             <div class="col-lg-8">
33               {% block left-panel-content %}
34                 {% for article in articles %}
35 
36                     <div class="row">
37                         <div class="col-lg-10">
38                             <h3>{{ article.title }}</h3>
39                             <div class="row">
40                                 <div class="col-md-2">
41                                      <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
42                                 </div>
43                                 <div class="col-md-2">
44                                     <i class="fa fa-comment-o" aria-hidden="true">55</i>
45                                 </div>
46                                 <hr style="border: 1px dashed darkgray">
47 
48                             </div>
49                         </div>
50                         <div class="col-lg-2">
51                             <img height="50px" width="50px" src="\static\{% truncate_upload_img article.head_img %}" >
52                         </div>
53                     </div>
54                 {% endfor %}
55                 <nav aria-label="...">
56                       <ul class="pagination">
57                         <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
58                             {% for page in articles.paginator.page_range %}
59                                 {% if articles.number == page %}
60                                     <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>
61                                 {% else %}
62 {#                                    <li class=""><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li>#}
63                                     {% render_paginator_btn articles page %}
64                                 {% endif %}
65                             {% endfor %}
66                       </ul>
67                 </nav>
68                 <p>共{{ articles.paginator.count }}篇文章</p>
69               {% endblock %}
70             </div>
71 
72             <div class="col-lg-4">
73                 right content
74             </div>
75         </div>
76          {% endblock %}
77     </div><!-- /.container -->
78 
79 {% endblock %}
index.html

 5.登录验证

 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r'^admin/', admin.site.urls),
23     url(r'^$', views.index ),
24     url(r'^category/(\d+|all)/$', views.category,name="category" ),
25     url(r'^article/(\d+)/$', views.article_detail,name="article_detail" ),
26     url(r'^new_article/$', views.new_article,name="new_article" ),
27     url(r'^account/login/$', views.acc_auth, name='acc_auth'),
28     url(r'^account/logout/$', views.acc_logout, name='acc_logout'),
29 ]
urls
 1 from django.shortcuts import render,HttpResponse,redirect
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 from django.contrib.auth import authenticate,logout,login
 5 from django.contrib.auth.decorators import login_required
 6 
 7 from bbs import forms
 8 # Create your views here.
 9 
10 
11 def index(request):
12     return redirect("/category/all/")
13 
14 def acc_logout(request):
15 
16     logout(request)
17 
18     return redirect("/account/login/")
19 
20 def acc_auth(request):
21     errors = {}
22     if request.method == "POST":
23         username = request.POST.get("username")
24         password = request.POST.get("password")
25         user = authenticate(username=username,password=password)
26         print("auth res", user)
27         if user:
28             print("authenticate success")
29             login(request,user)
30             return redirect(request.GET.get("next") or "/")
31         else:
32             errors = {"error":"Wrong username or password!"}
33 
34     return render(request,"login.html", errors)
35 
36 
37 def category(request,category_id):
38     categories = models.Category.objects.filter(set_as_top_menu=True)
39 
40     if category_id == "all":
41         articles = models.Article.objects.all().order_by("-id")
42     else:
43         articles = models.Article.objects.filter(category_id=category_id).order_by("-id")
44 
45     paginator = Paginator(articles, 5)  # Show 25 contacts per page
46 
47     page = request.GET.get('page')
48     try:
49         objs = paginator.page(page)
50     except PageNotAnInteger:
51         # If page is not an integer, deliver first page.
52         objs = paginator.page(1)
53     except EmptyPage:
54         # If page is out of range (e.g. 9999), deliver last page of results.
55         objs = paginator.page(paginator.num_pages)
56 
57     return render(request,'index.html', {"categories":categories, "articles":objs})
58 
59 
60 def article_detail(request,article_id):
61 
62     article_obj = models.Article.objects.get(id=article_id)
63 
64 
65     return  render(request,"article.html",{"article":article_obj})
66 
67 @login_required
68 def new_article(request):
69 
70     if request.method == "POST":
71         print("request post:",request.POST)
72         print("request files:",request.FILES,request.user )
73 
74 
75         article_form = forms.ArticleForm(data=request.POST,files=request.FILES)
76         if article_form.is_valid():
77             print("formdata", article_form.cleaned_data)
78             article_form.cleaned_data['author_id'] = request.user.id
79             # # #article_form.save()
80             tags = article_form.cleaned_data.pop("tags")
81             obj = models.Article(**article_form.cleaned_data)
82             obj.save()
83             obj.tags.add(*tags)
84 
85             obj.save()
86 
87             return HttpResponse('''<h3><a href="/article/%s/">%s</a></h3>''' % (obj.id,obj.title) )
88 
89     else:
90 
91         article_form = forms.ArticleForm()
92 
93     return  render(request,"new_article.html",{"form":article_form})
views

注意:以前一直作的是用户验证,其实并无登录,因此咱们在认证以后须要登录,这样才能显示用户登录。

 1 {% extends "base.html" %}
 2 
 3 
 4 {% block body %}
 5 
 6     <div class="container">
 7       <div class="col-lg-3 col-lg-offset-4">
 8           <form class="form-signin" method="post">
 9                 <h2 class="form-signin-heading">1024黄鳝社区</h2>
10                 <label for="inputEmail" class="sr-only">Username</label>
11                 <input type="text" name="username" class="form-control" placeholder="username" required autofocus>
12                 <label for="inputPassword" class="sr-only">Password</label>
13                 <input type="password"  name="password" id="inputPassword" class="form-control" placeholder="Password" required>
14 
15                 <span style="color: red">{{ error }}</span>
16                 <div class="checkbox">
17                   <label>
18                     <input type="checkbox" value="remember-me"> Remember me
19                   </label>
20                 </div>
21                 <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
22           </form>
23 
24       </div>
25 
26 
27     </div>
28 
29 
30 {% endblock %}
login。html

6.增长发帖功能,若是没有登录,先登录再发帖

 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r'^admin/', admin.site.urls),
23     url(r'^$', views.index ),
24     url(r'^category/(\d+|all)/$', views.category,name="category" ),
25     url(r'^article/(\d+)/$', views.article_detail,name="article_detail" ),
26     url(r'^new_article/$', views.new_article,name="new_article" ),
27     url(r'^account/login/$', views.acc_auth, name='acc_auth'),
28     url(r'^account/logout/$', views.acc_logout, name='acc_logout'),
29 ]
urls
  1 {% extends "base.html" %}
  2 {% load bbs_tags %}
  3 
  4 {% block body %}
  5     <nav class="navbar navbar-inverse navbar-fixed-top">
  6       <div class="container" >
  7         <div class="navbar-header">
  8           <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
  9             <span class="sr-only">Toggle navigation</span>
 10             <span class="icon-bar"></span>
 11             <span class="icon-bar"></span>
 12             <span class="icon-bar"></span>
 13           </button>
 14           <a class="navbar-brand" href="http://v3.bootcss.com/examples/starter-template/#">社区</a>
 15         </div>
 16         <div id="navbar" class="collapse navbar-collapse">
 17           <ul class="nav navbar-nav">
 18             <li class=""><a href="{% url 'category' 'all' %}">所有</a></li>
 19 
 20             {% for category in categories %}
 21 
 22                 <li class=""><a href="{% url 'category' category.id %}">{{ category.name }}</a></li>
 23             {% endfor %}
 24           </ul>
 25 
 26          <ul class="nav navbar-nav navbar-right">
 27              <li class=""><a href="{% url 'new_article'  %}">发帖</a></li>
 28              {% if request.user.is_authenticated %}
 29 
 30                  <li class="dropdown">
 31                   <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.user }} <span class="caret"></span></a>
 32                   <ul class="dropdown-menu">
 33                     <li><a href="{% url 'logout' %}">Logout</a></li>
 34                     <li><a href="#">Another action</a></li>
 35                   </ul>
 36                  </li>
 37              {% else %}
 38                 <li class=""><a href="{% url 'login'  %}">登陆</a></li>
 39              {% endif %}
 40 
 41 
 42          </ul>
 43 
 44         </div><!--/.nav-collapse -->
 45       </div>
 46     </nav>
 47 
 48     <div class="container" style="background-color: white;>
 49       {% block container %}
 50       <div class=" row" >
 51 
 52           <div  class="col-lg-8">
 53             {% block left-panel-content %}
 54               {% for article in articles %}
 55                 <div class="row">
 56 
 57                     <div class="col-lg-10">
 58                         <h3><a href="{% url 'article_detail'  article.id %}">{{ article.title }}</a></h3>
 59 
 60                         <div class="row">
 61                             <div class="col-md-2" >
 62                                     <i class="fa fa-thumbs-o-up" aria-hidden="true">444</i>
 63                             </div>
 64                             <div class="col-md-2">
 65                                     <i class="fa fa-comment-o" aria-hidden="true">42</i>
 66                             </div>
 67                         </div>
 68                         <hr style="border: 1px dashed darkgray">
 69                     </div>
 70                     <div class="col-lg-2">
 71                         <img height="75px" width="75px" src="/static/{% truncate_upload_img article.head_img %}" >
 72                     </div>
 73 
 74 
 75 
 76                 </div>
 77               {% endfor %}
 78 
 79 
 80 
 81               <nav aria-label="...">
 82                   <ul class="pagination">
 83 
 84                       {% for page in articles.paginator.page_range %}
 85                          {% if articles.number == page %}
 86                             <li class="active"><a href="?page={{ page }}">{{ page }} <span class="sr-only">(current)</span></a></li>
 87                         {% else %}
 88 {#                            <li class=""><a href="?page={{page}}">{{ page }} <span class="sr-only">(current)</span></a></li>#}
 89 
 90                              {% render_paginator_btn articles page %}
 91 
 92                         {% endif %}
 93                       {% endfor %}
 94 
 95                   </ul>
 96               </nav>
 97               <p>共{{ articles.paginator.count }}篇文章</p>
 98             {% endblock %}
 99           </div>
100           <div  class="col-lg-4">
101               right  content
102           </div>
103 
104       </div>
105       {% endblock %}
106     </div><!-- /.container -->
107 
108 {% endblock %}
index.html
 1 from django.shortcuts import render,HttpResponse,redirect
 2 from  bbs import models
 3 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 4 from django.contrib.auth import authenticate,logout,login
 5 from django.contrib.auth.decorators import login_required
 6 
 7 from bbs import forms
 8 # Create your views here.
 9 
10 
11 def index(request):
12     return redirect("/category/all/")
13 
14 def acc_logout(request):
15 
16     logout(request)
17 
18     return redirect("/account/login/")
19 
20 def acc_auth(request):
21     errors = {}
22     if request.method == "POST":
23         username = request.POST.get("username")
24         password = request.POST.get("password")
25         user = authenticate(username=username,password=password)
26         print("auth res", user)
27         if user:
28             print("authenticate success")
29             login(request,user)
30             return redirect(request.GET.get("next") or "/")
31         else:
32             errors = {"error":"Wrong username or password!"}
33 
34     return render(request,"login.html", errors)
35 
36 
37 def category(request,category_id):
38     categories = models.Category.objects.filter(set_as_top_menu=True)
39 
40     if category_id == "all":
41         articles = models.Article.objects.all().order_by("-id")
42     else:
43         articles = models.Article.objects.filter(category_id=category_id).order_by("-id")
44 
45     paginator = Paginator(articles, 5)  # Show 25 contacts per page
46 
47     page = request.GET.get('page')
48     try:
49         objs = paginator.page(page)
50     except PageNotAnInteger:
51         # If page is not an integer, deliver first page.
52         objs = paginator.page(1)
53     except EmptyPage:
54         # If page is out of range (e.g. 9999), deliver last page of results.
55         objs = paginator.page(paginator.num_pages)
56 
57     return render(request,'index.html', {"categories":categories, "articles":objs})
58 
59 
60 def article_detail(request,article_id):
61 
62     article_obj = models.Article.objects.get(id=article_id)
63 
64 
65     return  render(request,"article.html",{"article":article_obj})
66 
67 @login_required
68 def new_article(request):
69 
70     if request.method == "POST":
71         print("request post:",request.POST)
72         print("request files:",request.FILES,request.user )
73 
74 
75         article_form = forms.ArticleForm(data=request.POST,files=request.FILES)
76         if article_form.is_valid():
77             print("formdata", article_form.cleaned_data)
78             article_form.cleaned_data['author_id'] = request.user.id
79             # # #article_form.save()
80             tags = article_form.cleaned_data.pop("tags")
81             obj = models.Article(**article_form.cleaned_data)
82             obj.save()
83             obj.tags.add(*tags)
84 
85             obj.save()
86 
87             return HttpResponse('''<h3><a href="/article/%s/">%s</a></h3>''' % (obj.id,obj.title) )
88 
89     else:
90 
91         article_form = forms.ArticleForm()
92 
93     return  render(request,"new_article.html",{"form":article_form})
views
 1 {% extends "base.html" %}
 2 
 3 
 4 {% block body %}
 5 
 6     <div class="container">
 7       <div class="col-lg-3 col-lg-offset-4">
 8           <form class="form-signin" method="post">
 9                 <h2 class="form-signin-heading">1024黄鳝社区</h2>
10                 <label for="inputEmail" class="sr-only">Username</label>
11                 <input type="text" name="username" class="form-control" placeholder="username" required autofocus>
12                 <label for="inputPassword" class="sr-only">Password</label>
13                 <input type="password"  name="password" id="inputPassword" class="form-control" placeholder="Password" required>
14 
15                 <span style="color: red">{{ error }}</span>
16                 <div class="checkbox">
17                   <label>
18                     <input type="checkbox" value="remember-me"> Remember me
19                   </label>
20                 </div>
21                 <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
22           </form>
23 
24       </div>
25 
26 
27     </div>
28 
29 
30 {% endblock %}
login.html

点击发帖时,假如用户没有登录,会跳转到登录页面,在url中会出现accounts,在setting中设置成:LOGIN_URL ="/account/login/"

在点击发贴是,若是没有登录时,就会跳转到登录页面,在登录url中,会出现一个next参数,登录成功时,next会返回到用户登录以前的位置,因此,在url中要匹配next。

7.评论(树形评论,此处使用字典的深度查询)

之因此使用深度查询,是为了防止遍历时出错,查完一个分支再查另外一个.

若是遍历到A3-1,就会到A下查有没有本身的父亲节点,每遍历一个节点都会返回去查到本身的父节点.

第一步:将数据库的评论变为字典形式.

第二步:在前端显示的时候,要以递归的形式循环字典.

第三步:自定义html标签,在后台生成,直接传给前端

补充:字典排序

加入a是一个字典a.items就会变成一个元组,而后经过sort排序

sorted(a.items(),key=lambda x:x[0])

 在后台以递归的方式生成html

 

 1 {% extends "index.html" %}
 2 {% load bbs_tags %}
 3 
 4 
 5 {% block left-panel-content %}
 6 <h1>{{ article.title }}</h1>
 7 <p>{{ article.author }}</p>
 8 <div class="row">
 9     <div class="col-md-4">
10         {{ article.pub_date }}
11     </div>
12     <div class="col-md-2">
13         评论 {{ article.comment_set.count }}
14     </div>
15 
16 
17 </div>
18 
19 <div style="margin-top: 20px;border: 1px dashed grey;padding: 10px">
20 
21 {{ article.body |safe }}
22 
23 </div>
24 <div style="height: 500px;border: 1px dashed red">
25     {% load_comments article %}
26 
27 
28 </div>
29 
30 
31 
32 {% endblock %}
 1 from  django.template import Library
 2 from django.utils.safestring import mark_safe
 3 
 4 register = Library()
 5 
 6 @register.simple_tag
 7 def truncate_upload_img(img_src):
 8     print(dir(img_src))
 9     print(img_src.name)
10     return img_src.name.lstrip("/uploads/")
11 
12 
13 @register.simple_tag
14 def render_paginator_btn(articles,page):
15 
16     current_page = articles.number
17     if abs(current_page - page) <= 5 :#display button
18         ele = """<li ><a href="?page={page}">{page}</a></li>""".format(page=page)
19         return mark_safe(ele)
20 
21     return ''
22 def build_comment_tree(comment_dic,obj):
23     """递归的把每一个评论放到合适的层级里面"""
24     #首先,要确保顶级不在这个字典中
25     for k,v in comment_dic.items():
26         if obj.p_node == k:   #表明找到了他父亲,把本身加到k下面
27             comment_dic[k][obj] = {}
28         else:#开始进行深度查询
29             build_comment_tree(comment_dic[k],obj)
30 
31 def build_comment_html(comment_dic,margin_arg):
32     """循环评论的字典,拼接html"""
33     comment_eles = ''
34     for k,v in comment_dic.items():
35         comment_eles += """<div style='border:1px dashed block;margin-left:{margin}px'>{user} --- {date} --- {comment}</div>"""\
36                 .format(user=k.user,
37                         date=k.date.strftime('%Y-%m-%d %H:%M:%S'),
38                         comment=k.comment,
39                         margin=margin_arg)
40         if v:#有下一级
41             comment_eles += build_comment_html(comment_dic[k],margin_arg+10)
42     return comment_eles
43 
44 @register.simple_tag
45 def load_comments(article_obj):
46     #先把数据库中全部的这边文章评论查出来,转成字典
47     #递归循环字典,生成html评论元素
48     comment_dic = {}
49     #key:父级
50     comment_objs = article_obj.comment_set.all().order_by('date')   #评论列表,以时间来排序,这样就不会出现,后评论的在列表的前面
51     for obj in comment_objs:
52         #判断obj有没有p_node,若是没有他本身就是顶级的评论,放到字典的第一层
53         if not obj.p_node:
54             comment_dic[obj] = {}      #此处生成空字典是为了他的字评论准备的
55         else:
56             build_comment_tree(comment_dic,obj)
57 
58     comment_list = sorted(comment_dic.items(),key=lambda x:x[0].date)
59     comment_html = """"""
60     for commnet_branch in comment_list:
61         margin_arg=0
62         branch_ele = """<div style='border:1px dashed black;margin-left:{margin}px'>{user} --- {date} --- {comment}</div>""".\
63             format(user=commnet_branch[0].user,
64                    date=commnet_branch[0].date.strftime('%Y-%m-%d %H:%M:%S'),
65                    comment=commnet_branch[0].comment,
66                    margin=margin_arg)
67         comment_html += branch_ele
68         #开始构建他的子集评论
69         comment_html += build_comment_html(commnet_branch[1],margin_arg+10)
70     return mark_safe(comment_html)
bbs_tag