建立表結構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 進階操做
包含: 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
以發郵件爲例:
先後端都須要驗證,前端驗證目的是減少後端的驗證壓力
經過後端來實現,而後傳給後端
1 urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 url(r'^mail$',views.mail) 4 ]
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})
須要咱們自行建立一個前端頁面,經過這個頁面可實現基本驗證,並且也不用再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})
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>
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>
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') #此處是爲了驗證後獲得乾淨的數據,業務邏輯用
對多個字段進行驗證
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
對手機號進行驗證
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']
默認只的設置,能夠設置多個,用逗號隔開
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})
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',]
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})
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
若是想讓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)
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">«</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 %}
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就返回空格
點擊進入,查看內容
繼承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 %}
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})
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 ]
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">«</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 %}
文章樣式設置(富文本編輯器)
使用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 %}
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>
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')
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 ]
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">«</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 %}
接下來,經過富文本編輯建立文章
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">«</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 %}
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})
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__'
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 %}
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})
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__'
還有一種方法是,直接將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 %}
<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})
{{ 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 ]
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})
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">«</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 %}
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 ]
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})
注意:以前一直作的是用戶驗證,其實並無登錄,因此咱們在認證以後須要登錄,這樣才能顯示用戶登錄。
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 %}
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 ]
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 %}
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})
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 %}
點擊發帖時,假如用戶沒有登錄,會跳轉到登錄頁面,在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)