tips:django官方中文文檔(http://python.usyiyi.cn/django/index.html),django基礎教程(http://www.ziqiangxuetang.com/django/django-tutorial.html)html
django是一個python中的web框架,若是想更好的使用它,最好還能有HTML, CSS, JavaScript等方面的知識。它的特色主要體如今幾個方面:強大的數據庫(具備許多api同時支持執行sql語句)、自帶強大的後臺功能、利用正則表達式匹配網址(簡化網頁管理)、模板與視圖一一對應、緩存機制、國際化支持等等。前端
django適合於敏捷開發,只須要少許代碼便可創建高性能web應用。因其已經完成了開發網站的許多常見代碼,因此減小了代碼的重複度。Ok,讓咱們首先看下django的主要文件組成。主要文件包括以下幾個py文件:node
文件 | 功能 |
urls.py | 網址入口,關聯到views中對於的函數 |
models.py | 與數據庫操做相關,創建應用數據模型 |
views.py | 處理用戶發出請求,從urls中對應過來,經過渲染templates中網頁顯示內容 |
settings.py | 相關設置,包括數據庫設置,郵件設置,靜態文件配置等 |
forms.py | 表單,用戶在瀏覽器端提交的表單數據類 |
admin.py | 後臺代碼,大部分已完成 |
環境搭建這一部分就不做敘述,網上教程不少。經常使用命令以下:python
1 #新建django project 2 django-admin.py startproject project-name 3 #新建django app命令 4 django-admin.py startapp app-name (or) python manage.py startapp app-name 5 #同步數據庫 6 python manage.py syncdb 7 #(django1.7.1及以上版本須要使用如下命令) 8 python manage.py makemigrations 9 python manage.py migrate 10 #運行django項目,不加port默認爲8000 11 python manage.py runserver port 12 #ex: python manage.py runserver 8080 13 #清空數據庫 14 python manage.py flush 15 #建立超級管理員 16 python manage.py createsuperuser 17 #修改管理員密碼 18 python manage.py changepassword username 19 #導出數據 20 python manage.py dumpdata appname > appname.json 21 #導入數據 22 python manage.py loaddata appname.json 23 #進入django項目命令終端 24 python manage.py shell 25 #進入數據庫命令行 26 python manage.py dbshell
咱們已經知道views中的函數是與urls中的網址對應的,接下來咱們作一個簡單的實例演示。mysql
1. 首先將應用名稱添加到settings.py中git
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'newReverse', 'reverse', )
2. 在views中定義視圖函數github
def test(request): return render(request, 'test.html')
這裏返回的結果有多種,其本質返回的結果是HttpResponse。其餘返回狀況如web
return HttpResponse("hello world") return render_to_response('test.html', '') return HttpResponseRedirect('/index/')
其中render與render_to_respose是同樣的,只是render不須要說明ResponseContext而render_to_respose則須要coding出來。render,最後會返回一個字典,以向模板中傳遞數據。ajax
return render_to_response('checkmt.html', {'info': info}) return render(request, 'day2.html', {'info': info})
3. 設置在urls.py中的地址正則表達式
urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), url(r'^login/$', login), url(r'^index/$', views.index, name="index"), )
這裏urls設置有多種方式,具體能夠查看官方文檔,其主要包括兩部分,正則表達式格式的網址+views函數。同時網址中還能夠傳遞參數。
模板是經過views函數渲染的,其實質是一個html網頁。通常放在app下的templates中,加載模板時django會自動尋找。那麼假如一個工程中有多個應用,每一個應用的templates中都有一個index.html,而後直接調用render(request, 'index.html')能不能找到呢?答案是可能會出錯。讓咱們來探究下django模板查找機制。django模板查找過程是在每一個app的templates文件夾中查找(而不是當前app中的templates),當在這些應用的templates文件夾列表中查找到index.html模板時中止,因此說可能出現錯誤;若是最終沒有找到則返回一個templates not found錯誤。
接下來咱們來介紹一下關於模板代碼的應用。通常地,網站中都會有一些模板設計,即不少網頁都有不少相同的部分(如導航、底部、時間等),這時就能夠用使用模板來減小代碼重複度了。如假設有一個base.html
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>{% block title %}默認標題{% endblock %} - 自強學堂</title> 5 </head> 6 <body> 7 8 {% include 'nav.html' %} 9 10 {% block content %} 11 <div>這裏是默認內容,全部繼承自這個模板的,若是不覆蓋就顯示這裏的默認內容。</div> 12 {% endblock %} 13 14 {% include 'bottom.html' %} 15 16 {% include 'tongji.html' %} 17 18 </body> 19 </html>
block就是模板能夠重寫的部分,include即包含其餘文件內容,因此咱們就能夠設計以下index.html
1 {% extends 'base.html' %} 2 3 {% block title %}歡迎光臨首頁{% endblock %} 4 5 {% block content %} 6 {% include 'ad.html' %} 7 歡迎光臨 8 {% endblock %}
django模板還提供了用於處理傳遞過來的字典數據的技術,如循環、條件判斷、過濾器、經常使用標籤等。以下就是這些技術的一些相關應用。
<!-- 雙重循環加載表格數據, if判斷輸出什麼數據--> <table border="1"> <tr> <td>會議室\時間</td> {%for each in info.time%} <td>{{each}}</td> {%endfor%} </tr> {% for each in info.rvsdest %} <tr> <td>{{each.mtrname}}</td> {% for ev in each.time%} {% if ev == 1%} <td>NO</td> {%else%} <td></td> {%endif%} {% endfor%} </tr> {% endfor%} </table>
其餘的諸如(==, !=, >=, <=, >, <, and, or, not, in, not in)等也能夠在模板中使用。
Django模型與數據庫相關,其相關代碼通常寫在models.py中。django支持的數據庫包括sqlite3,mysql,PostgreSQL等,只需在settings.py中配置便可,同時數據模型提供了豐富的api。以下便可settings.py的配置樣例。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'reverse', 'USER': 'root', 'PASSWORD': '******', 'HOST': '127.0.0.1', 'PORT': '3306', } }
1. 關於數據模型,首先咱們來介紹下field,以下爲一個model實例
1 class RoomReverse(models.Model): 2 mtno = models.IntegerField(default=0) 3 STATE_CHOICES = (('st', 'stale'), ('ing', 'underway'), ('ns', 'notstart'), ) 4 state = models.CharField(max_length=20, choices=STATE_CHOICES, default='ns') 5 rvstime = models.DateTimeField(auto_now_add=True) 6 mtdate = models.CharField(max_length=100, default=None) 7 mtrno = models.ForeignKey(Meetingroom) 8 emails = models.TextField(default=None) 9 rpemail = models.EmailField(default=None) 10 class Meta: 11 table_name = 'RoomReverse' 12 def __getstr__(): 13 return str(mtno)
其中包括的field有CharField、DateTimeField、IntegerField、TextField、ForeignKey、EmailField等等,其能夠實現數據庫的主鍵設置、外鍵設置,還能實現一對多、多對1、多對多設置等。另外一方面,若是以爲django自帶的field不夠用,還可使用本身定義的field。
2. 接下來咱們來介紹下關於數據庫的一些操做。首先是將數據模型同步到數據庫操做,命令以下:
1 python manage.py syncdb 2 #(django1.7.1及以上版本須要使用如下命令) 3 python manage.py makemigrations 4 python manage.py migrate
3. 對數據進行增刪改查操做,通常的方法有:
#新建對象 User.objects.create(name=name,pwd=pwd) #1 #處理重複,是否建立 User.objects.get_or_create(name=name,pwd=pwd) #用save建立 us = User(name=name,pwd=pwd) us.save() #批量建立 User.objects.bulk_create(user_object_list)
User.objects.all()#獲取所有對象 User.objects.all()[:10]#切片操做 User.objects.get(name=name1)#獲取單個數據,第一個查找到的數據 User.objects.filter(name=name1)#=User.objects.filter(name_exact=name1)#獲取多個數據 User.objects.filter(name__iexact="abc")#不區分大小寫 User.objects.filter(name__contains="abc")#name包含abc User.objects.filter(name__icontains="abc")#name不包含abc,且不區分大小寫 User.objects.filter(name__regex="^abc")#正則表達式查詢 User.objects.filter(name__iregex="^abc")#正則表達式不區分大小寫 User.objects.exclude(name__contains="abc")#排除包含abc的對象 User.objects.filter(name__contains="abc").exclude(pwd=111)#name等於abc但pwd不等於111的對象
User.objects.filter(name__in=[])#至關於將全部name在列表中的對象提取出來
4. 對django中的數據模型,存在一些內部方法來設置或修改模型屬性值
model._meta.get_fields() #獲取全部屬性 model._meta.get_field_by_name('id') #按屬性名獲取屬性 model._meta.get_all_field_names() #獲取模型全部屬性名稱 object.__setattr__(attr, data) #按屬性名設置模型對象對應的屬性值 getattr(model, attr) #獲取模型某一列屬性
5. 在模型對象中還能夠設置虛類來實現繼承方法
1 class BaseInfo(models.Model): 2 ''' 3 模板基本信息,包括建立時間、更新時間、更新人、操做人,爲虛類 4 ''' 5 create_time = models.DateTimeField(auto_now_add=True) 6 update_time = models.DateTimeField(auto_now=True) 7 ISVALID = ((CHECKING, 0), (VALID, 1), (INVALID, 2)) 8 valid = models.IntegerField(choices=ISVALID, default=CHECKING) 9 creator = models.CharField(max_length=30, default=None) 10 operator = models.CharField(max_length=30, default=None) 11 class Meta: 12 abstract = True 13 14 15 # 單個節點 16 class NodeData(BaseInfo): 17 id = models.AutoField(verbose_name='配置id', primary_key=True, auto_created=True) 18 script = models.CharField(verbose_name='中文簡述', max_length=50, default=None) 19 category = models.CharField(verbose_name='類別', max_length=30, default=None) 20 ensuper_class = models.CharField(verbose_name='實體父類', max_length=30, default=None) 21 evsuper_class = models.CharField(verbose_name='事件父類', max_length=30, default=None) 22 prsuper_class = models.CharField(verbose_name='屬性父類', max_length=30, default=None) 23 roleid = models.CharField(verbose_name='角色宿主', max_length=30, default=None) 24 define_area = models.CharField(verbose_name='定義域', max_length=50, default=None) 25 value_area = models.CharField(verbose_name='值域', max_length=50, default=None) 26 location_area = models.CharField(verbose_name='位置域', max_length=50, default=None) 27 statement = models.CharField(verbose_name='聲明', max_length=50, default=None) 28 illustrate = models.CharField(verbose_name='舉例', max_length=50, default=None) 29 combination = models.CharField(verbose_name='中文簡述 + 類別', max_length=80, default=None, unique=True) 30 31 class Meta(BaseInfo.Meta): 32 # managed = True 33 db_table = 'node_data' 34 35 def toJSON(self): 36 fields = [] 37 for field in self._meta.fields: 38 fields.append(field.name) 39 d = {} 40 for attr in fields: 41 if isinstance(getattr(self, attr), datetime.datetime): 42 d[attr] = getattr(self, attr).strftime('%Y-%m-%d %H:%M:%S') 43 elif isinstance(getattr(self, attr), datetime.date): 44 d[attr] = getattr(self, attr).strftime('%Y-%m-%d') 45 else: 46 d[attr] = getattr(self, attr) 47 return json.dumps(d)
Class-based views是Django爲解決建站過程當中的常見的呈現模式而創建的。具備以下幾個原則:
1. 使用django自身的cbvs
cbvs是可擴展的,但在也增長了複雜度,有時甚至出現8個import引入關係。django自帶的view以下表所示:
類名 | 功能 | 例子 |
---|---|---|
View | 基本View, 能夠在任什麼時候候使用 | 見後面詳細介紹 |
RedirectView | 從新定向到其餘URL | 將訪問"/log-in/"的用戶從新定向到"/login/" |
TemplateView | 顯示Django HTML template | 通常網站中使用模板顯示的頁 |
ListView | 顯示對象列表 | 文章列表頁 |
DetailView | 顯示對象詳情 | 文章詳細頁 |
FormView | 提交From | 網站聯繫咱們或emai訂閱form |
CreateView | 建立對象 | 建立新文章頁 |
UpdateView | 更新對象 | 修改文章頁 |
DeleteView | 刪除對象 | 刪除文章頁 |
Generic date views | 顯示一段時間內的對象 | 按時間歸類的博客 |
2. View中基本元素
1 class ModelCreatView(CreateView): 2 def __init__(self, model, template_name, context_object_name, success_url, success_msg, fields): 3 """ 4 初始化函數,這裏以creatview爲例,其餘view大同小異 5 :param model: 對應要操做的模型 6 :param template_name: 對應的模板名稱,通常爲html頁面 7 :param context_object_name: 返回給前臺的對象 8 :param success_url: 操做成功定向地址 9 :param success_msg: 返回的成功信息 10 :param fields: 須要操做的對應的模型中的屬性 11 """ 12 self.model = model 13 self.template_name = template_name 14 self.context_object_name = context_object_name 15 self.success_url = success_url 16 self.success_msg = success_msg 17 self.fields = fields 18 # 19 # self.model = MODEL 20 # self.fields = ['event_attribute', 'role_attribute', 'entiry_value', 'event_type', 'direction_flag', 'unique_flag', 21 # 'appear_flag'] 22 # self.context_object_name = 'knowledgeGraph' 23 # self.success_url = '/knowledgeGraph/' 24 # self.template_name = 'knowledge_graph/add.html'
3. ListView簡介
listview是一個展現列表的view,返回的是一個template,包含兩個關鍵方法:
def get_context_data(self, **kwargs):
def get_queryset(self):
第一個方法返回一個字典給前端,包括分頁信息,列表信息,已經其餘自定義的信息;第二個方法返回數據庫中獲取到的數據(可能通過條件迭代),具體實現的實例代碼以下:
1 def get_context_data(self, **kwargs): 2 """ 3 用來獲取返回給前端頁面的dict數據,前端頁面可直接經過應用dict中的key來獲取value 4 :param kwargs: 5 :return: context,一個字典類型的數據 6 """ 7 try: 8 context = super(ModelListView, self).get_context_data(**kwargs) 9 log.debug("get_context_data") 10 page_obj = context['page_obj'] 11 pages = [] 12 for i in range(1, page_obj.paginator.num_pages + 1): 13 if i == 1 or i == page_obj.paginator.num_pages: 14 pages.append(i) 15 elif i >= page_obj.number - 2 and i <= page_obj.number + 2: 16 pages.append(i) 17 elif pages[-1] != None: 18 pages.append(None) 19 else: 20 pass 21 context['pages'] = pages 22 context['order_type'] = self.order_type 23 context['length'] = len(self.object_list) 24 if 'pagination_num' not in context: 25 context['pagination_num'] = self.paginate_by 26 self.echo_search(context) 27 if self.model == NodeData: 28 context['tree'] = self.get_tree() 29 return context
4. Mixin實現
view中若是以爲自帶的post、get方法不夠好,也能夠重寫post、get方法,這樣就與函數式編程沒有什麼區別。有時須要先後端異步方式加載數據,就須要使用ajax來完成,這時就可使用mixin來解決。
使用mixin能夠爲class提供額外的功能,但它自身卻不能單獨使用的類. 在具備多繼承能力的編程語言中, mixin能夠爲類增長額外功能或方法. 在Django中, 咱們可使用mixin爲CBVs提供更多的擴展性, 固然在類繼承過程當中, 咱們推薦如下原則:
class ModelValidView(LoginRequiredMixin, AjaxResponseMixin, View): def __init__(self, model): self.model = model def post_ajax(self, request, *args, **kwargs): pass
7、文件上傳下載
django中文件上傳通常是將文件拷本到服務器上,而後訪問服務器上數據;下載文件則能夠直接經過訪問url完成。
1. 文件上傳:
前端:
1 {% include "./_meta.html" %} 2 {% load staticfiles %} 3 4 5 </head> 6 <body> 7 <article class="page-container"> 8 <form action="{% url 'freedomSegger_fileupload' %}" method="post" class="form form-horizontal" id="form-admin-role-add" enctype="multipart/form-data">{% csrf_token %} 9 <div class="row cl"> 10 <label class="form-label col-xs-4 col-sm-3">模板下載:</label> 11 <div class="formControls col-xs-8 col-sm-9"> 12 <a href="TODO">點我下載</a> 13 </div> 14 </div> 15 <div class="row cl"> 16 <label class="form-label col-xs-4 col-sm-3"><span class="c-red">*</span>上傳文件:</label> 17 <div class="formControls col-xs-8 col-sm-9"> 18 <input type="file" class="input-text" name="file"> 19 </div> 20 </div> 21 <div class="row cl"> 22 <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3"> 23 <button type="submit" class="btn btn-success radius" id="admin-role-save" name="admin-role-save"><i class="icon-ok"></i> 肯定</button> 24 </div> 25 </div> 26 </form> 27 </article> 28 29 {% include "./_footer.html" %} 30 31 </body> 32 </html>
後臺:
1)工具方法:
1 def handle_uploaded_file(f): 2 """ 3 將上傳的文件寫入服務器對應目錄下 4 :param f: 上傳文件 5 :return: 6 """ 7 file_name = "" 8 try: 9 path = "uplodfile" + time.strftime('/%Y/%m/%d/%H/%M/%S/') 10 if not os.path.exists(path): 11 os.makedirs(path) 12 file_name = path + f.name 13 destination = open(file_name, 'wb+') 14 for chunk in f.chunks(): 15 destination.write(chunk) 16 destination.close() 17 except Exception, e: 18 print e 19 return file_name
2)加載上傳頁面
1 @login_required 2 def tofileUpload(request): 3 return render(request, "knowledge_graph/file_upload.html", )
3)上傳文件並存入數據庫中
1 @login_required 2 def fileUpload(request): 3 success_url = '/knowledgeGraph/' 4 failed_url = '/knowledgeGraph/toFileUpload' 5 return common_file_upload(request, save_data, success_url, failed_url, MODEL) 6 7 def common_file_upload(request, save_data, success_url, failed_url, MODEL): 8 """ 9 批量導入文件 10 :param request: 11 :param save_data: 針對每一個model,在對應的view文件中自定義 12 :param success_url: 導入成功時返回的url 13 :param failed_url: 導入失敗時返回的url 14 :param MODEL: 對應的模型 15 :return: 返回對應的頁面 16 """ 17 file_path = file_upload.handle_uploaded_file(request.FILES['file']) 18 try: 19 text = list(set(open(file_path).readlines())) 20 except Exception as e: 21 log.error(MODEL._meta.object_name + " 文件打開錯誤"+str(e)) 22 messages.info(request, "文件打開錯誤"+str(e)) 23 return HttpResponseRedirect(failed_url) 24 except_info, failed = save_data(text, request) 25 if failed == 0: 26 messages.success(request,"400") 27 messages.info(request, "導入成功") 28 log.info(MODEL._meta.object_name + " 文件上傳成功") 29 return HttpResponseRedirect(success_url) 30 else: 31 log.warning(MODEL._meta.object_name + " " + except_info) 32 messages.info(request, except_info) 33 return HttpResponseRedirect(failed_url)
4)url設置
1 # toFileUpload 2 url(r'^knowledgeGraph/toFileUpload/$', knowledge_graph.tofileUpload, name="knowledgeGraph_to_fileupload"), 3 # FileUpload 4 url(r'^knowledgeGraph/FileUpload/$', knowledge_graph.fileUpload, name="knowledgeGraph_fileupload"),
2. 文件下載:
文件下載可經過直接的url訪問而後返回response完成,注意此時不能使用ajax異步操做。
1)下載單個文件:
1 def download_file(request,data): 2 try: 3 write_model(data) 4 file_name = "/".join(str(os.path.dirname(__file__)).replace("\\", "/").split("/")[0:-2]) + "/download/" + data 5 wrapper = FileWrapper(file(file_name)) 6 response = HttpResponse(wrapper, content_type="application/octet-stream") 7 response['Content-Length'] = os.path.getsize(file_name) 8 response['Content-Disposition'] = 'attachment;filename=%s'% data 9 messages.info(request, "下載成功") 10 log.info("下載成功") 11 return response 12 except Exception as e: 13 messages.warning(request, "下載失敗 " + str(e)) 14 log.warning("下載失敗 " + str(e)) 15 return HttpResponseRedirect("/index/")
2)下載多個文件,此時通常狀況下使用zip下載,或者使用服務器推送服務(並不知道怎麼實現)
這種方法是網上能夠輕鬆找到,可是我實現的時候每次下載的zip包大小都是0kb,經過調試,猜想應該是臨時文件問題,沒法把臨時文件放入壓縮包內,致使壓縮包大小爲空。
1 def download_zipfile(request,data): 2 3 try: 4 write_model(model="KnowledgeGraph") 5 temp = tempfile.TemporaryFile() 6 archive = zipfile.ZipFile(temp, mode='w') 7 dir_name = "/".join(str(os.path.dirname(__file__)).replace("\\", "/").split("/")[0:-2]) + "/download/" 8 filelist = [] 9 for root, dirlist, files in os.walk(dir_name): 10 for filename in files: 11 filelist.append(os.path.join(root, filename)) 12 for eachfile in filelist: 13 destfile = eachfile[len(dir_name):] 14 print "Zip file %s..." % destfile 15 archive.write(eachfile, destfile) 16 archive.close() 17 wrapper = FileWrapper(temp) 18 response = http.HttpResponse(wrapper, content_type='application/x-zip-compressed') 19 response['Content-Disposition'] = 'attachment; filename=model.zip' 20 response['Content-Length'] = temp.tell() 21 print temp.tell() 22 temp.seek(0) 23 return response 24 except Exception as e: 25 print e 26 messages.info(request, "下載失敗") 27 return HttpResponseRedirect("/index/")
第二種方法成功實現壓縮包下載,這種方法是在github上找到,供參考:
1 def download_zipfile(request,data): 2 try: 3 dir_name = "/".join(str(os.path.dirname(__file__)).replace("\\", "/").split("/")[0:-2]) + "/download/" 4 files = [] 5 for ev in data: 6 input_file = dir_name + ev 7 text = open(input_file).readlines() 8 #建立臨時文件 9 output_file = tempfile.NamedTemporaryFile(mode='w+b', prefix='base', suffix='.txt', delete=False) 10 #output_file = tempfile.NamedTemporaryFile(mode='w+b', prefix='editor', suffix='.xml', delete=False) 11 output_file.writelines(text) 12 files.append(output_file.name) 13 output_file.close() 14 zip_filename = "model.zip" 15 s = StringIO.StringIO() 16 zf = zipfile.ZipFile(s, "a") 17 #將臨時文件加入zip包 18 for i in range(0, len(files)): 19 #設置文件名 20 zip_path = os.path.join(data[i]) 21 zf.write(files[i], zip_path) 22 zf.close() 23 response = HttpResponse(s.getvalue(), content_type="application/x-zip-compressed") 24 response['Content-Disposition'] = 'attachment; filename=%s' % zip_filename 25 messages.info(request, "下載成功") 26 log.info("下載成功") 27 return response 28 except Exception as e: 29 messages.warning(request, "下載失敗 " + str(e)) 30 log.warning("下載失敗 " + str(e)) 31 return HttpResponseRedirect("/index/")