Эх сурвалжийг харах

更新了Django第4天代码

jackfrued 7 жил өмнө
parent
commit
39f70bd33d

+ 3 - 0
Day31-Day35/car/car/__init__.py

@@ -0,0 +1,3 @@
+import pymysql
+
+pymysql.install_as_MySQLdb()

+ 127 - 0
Day31-Day35/car/car/settings.py

@@ -0,0 +1,127 @@
+"""
+Django settings for car project.
+
+Generated by 'django-admin startproject' using Django 2.0.5.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.0/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/2.0/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'ol6dmf6im(w!l*z4w+_whm&)8@(c7%4&tlhd%uh6$lfx=pi*5e'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'search',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'car.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates')]
+        ,
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'car.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql',
+        'NAME': 'car',
+        'HOST': 'localhost',
+        'PORT': 3306,
+        'USER': 'root',
+        'PASSWORD': '123456',
+    }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.0/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.0/howto/static-files/
+STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
+
+STATIC_URL = '/static/'

+ 26 - 0
Day31-Day35/car/car/urls.py

@@ -0,0 +1,26 @@
+"""car URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.conf.urls import url
+
+from search import views
+
+urlpatterns = [
+    url(r'^search$', views.search),
+    url(r'^search2$', views.ajax_search),
+    url(r'^add', views.add),
+    url(r'^admin/', admin.site.urls),
+]

+ 16 - 0
Day31-Day35/car/car/wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for car project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car.settings")
+
+application = get_wsgi_application()

+ 15 - 0
Day31-Day35/car/manage.py

@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car.settings")
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)

+ 0 - 0
Day31-Day35/car/search/__init__.py


+ 12 - 0
Day31-Day35/car/search/admin.py

@@ -0,0 +1,12 @@
+from django.contrib import admin
+
+from search.models import CarRecord
+
+
+class CarRecordAdmin(admin.ModelAdmin):
+
+    list_display = ('carno', 'reason', 'date', 'punish', 'isdone')
+    search_fields = ('carno', )
+
+
+admin.site.register(CarRecord, CarRecordAdmin)

+ 5 - 0
Day31-Day35/car/search/apps.py

@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class SearchConfig(AppConfig):
+    name = 'search'

+ 30 - 0
Day31-Day35/car/search/migrations/0001_initial.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2018-05-24 01:16
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='CarRecord',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('carno', models.CharField(max_length=7)),
+                ('reason', models.CharField(max_length=50)),
+                ('date', models.DateTimeField(db_column='happen_date')),
+                ('punlish', models.CharField(max_length=50)),
+                ('isdone', models.BooleanField(default=False)),
+            ],
+            options={
+                'db_table': 'tb_car_record',
+            },
+        ),
+    ]

+ 24 - 0
Day31-Day35/car/search/migrations/0002_auto_20180524_1420.py

@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2018-05-24 06:20
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('search', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='carrecord',
+            options={'ordering': ('-date',)},
+        ),
+        migrations.RenameField(
+            model_name='carrecord',
+            old_name='punlish',
+            new_name='punish',
+        ),
+    ]

+ 0 - 0
Day31-Day35/car/search/migrations/__init__.py


+ 22 - 0
Day31-Day35/car/search/models.py

@@ -0,0 +1,22 @@
+from django.db import models
+
+
+class CarRecord(models.Model):
+    carno = models.CharField(max_length=7)
+    reason = models.CharField(max_length=50)
+    date = models.DateTimeField(db_column='happen_date', auto_now_add=True)
+    punish = models.CharField(max_length=50)
+    isdone = models.BooleanField(default=False)
+
+    @property
+    def happen_date(self):
+        return self.date.strftime('%Y-%m-%d %H:%M:%S')
+        """
+        return '%d年%02d月%02d日 %02d:%02d:%02d' % \
+               (self.date.year, self.date.month, self.date.day,
+                self.date.hour, self.date.minute, self.date.second)
+        """
+
+    class Meta:
+        db_table = 'tb_car_record'
+        ordering = ('-date', )

+ 3 - 0
Day31-Day35/car/search/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 78 - 0
Day31-Day35/car/search/views.py

@@ -0,0 +1,78 @@
+from json import JSONEncoder
+
+from django import forms
+from django.http import JsonResponse
+from django.shortcuts import render
+
+from search.models import CarRecord
+
+# 序列化/串行化/腌咸菜 - 把对象按照某种方式处理成字节或者字符的序列
+# 反序列化/反串行化 - 把字符或者字节的序列重新还原成对象
+# Python实现序列化和反序列化的工具模块 - json / pickle / shelve
+# return HttpResponse(json.dumps(obj), content_type='application/json')
+# return JsonResponse(obj, encoder=, safe=False)
+# from django.core.serializers import serialize
+# return HttpResponse(serialize('json', obj), content_type='application/json; charset=utf-8')
+
+
+class CarRecordEncoder(JSONEncoder):
+
+    def default(self, o):
+        del o.__dict__['_state']
+        o.__dict__['date'] = o.happen_date
+        return o.__dict__
+
+
+def ajax_search(request):
+    if request.method == 'GET':
+        return render(request, 'search2.html')
+    else:
+        carno = request.POST['carno']
+        record_list = list(CarRecord.objects.filter(carno__icontains=carno))
+        # 第一个参数是要转换成JSON格式(序列化)的对象
+        # encoder参数要指定完成自定义对象序列化的编码器(JSONEncoder的子类型)
+        # safe参数的值如果为True那么传入的第一个参数只能是字典
+        # return HttpResponse(json.dumps(record_list), content_type='application/json; charset=utf-8')
+        return JsonResponse(record_list, encoder=CarRecordEncoder,
+                            safe=False)
+
+
+def search(request):
+    # 请求行中的请求命令
+    # print(request.method)
+    # 请求行中的路径
+    # print(request.path)
+    # 请求头(以HTTP_打头的键是HTTP请求的请求头)
+    # print(request.META)
+    # 查询参数: http://host/path/resource?a=b&c=d
+    # print(request.GET)
+    # 表单参数
+    # print(request.POST)
+    if request.method == 'GET':
+        ctx = {'show_result': False}
+    else:
+        carno = request.POST['carno']
+        ctx = {
+            'show_result': True,
+            'record_list': list(CarRecord.objects.filter(carno__contains=carno))}
+    return render(request, 'search.html', ctx)
+
+
+class CarRecordForm(forms.Form):
+    carno = forms.CharField(min_length=7, max_length=7, label='车牌号', error_messages={'carno': '请输入有效的车牌号'})
+    reason = forms.CharField(max_length=50, label='违章原因')
+    punish = forms.CharField(max_length=50, required=False, label='处罚方式')
+
+
+def add(request):
+    errors = []
+    if request.method == 'GET':
+        f = CarRecordForm()
+    else:
+        f = CarRecordForm(request.POST)
+        if f.is_valid():
+            CarRecord(**f.cleaned_data).save()
+            f = CarRecordForm()
+        else:
+            errors = f.errors.values()
+    return render(request, 'add.html', {'f': f, 'errors': errors})

+ 3 - 0
Day31-Day35/car/static/images/icon-no.svg

@@ -0,0 +1,3 @@
+<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
+  <path fill="#dd4646" d="M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
+</svg>

+ 3 - 0
Day31-Day35/car/static/images/icon-yes.svg

@@ -0,0 +1,3 @@
+<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
+  <path fill="#70bf2b" d="M1412 734q0-28-18-46l-91-90q-19-19-45-19t-45 19l-408 407-226-226q-19-19-45-19t-45 19l-91 90q-18 18-18 46 0 27 18 45l362 362q19 19 45 19 27 0 46-19l543-543q18-18 18-45zm252 162q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
+</svg>

+ 23 - 0
Day31-Day35/car/templates/add.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>添加</title>
+</head>
+<body>
+    <h2>添加违章记录</h2>
+    <hr>
+    <p>
+        {% for err in errors %}
+        <div style="color: red; font-size: 12px;">{{ err }}</div>
+        {% endfor %}
+    </p>
+    <form action="/add" method="post">
+        <table>
+        {{ f.as_table }}
+        </table>
+        {% csrf_token %}
+        <input type="submit" value="添加">
+    </form>
+</body>
+</html>

+ 90 - 0
Day31-Day35/car/templates/search.html

@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+{% load staticfiles %}
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>车辆违章查询</title>
+    <style>
+        * {
+            font: 18px/30px Arial;
+        }
+        #container {
+            width: 960px;
+            margin: 0 auto;
+        }
+        #container form {
+            width: 620px;
+            margin: 10px auto;
+            padding-top: 100px;
+        }
+        #container form input[type=search] {
+            display: inline-block;
+            width: 480px;
+            height: 30px;
+        }
+        #container form input[type=submit] {
+            display: inline-block;
+            width: 80px;
+            height: 40px;
+            border: None;
+            background-color: red;
+            color: white;
+            margin-left: 20px;
+        }
+        #container table {
+            width: 920px;
+            margin: 20px auto;
+            border-collapse: collapse;
+        }
+        #container table th {
+            font-weight: bolder;
+            border-bottom: 1px solid darkgray;
+        }
+        #container table td {
+            text-align: center;
+            height: 50px;
+            width: 180px;
+        }
+    </style>
+</head>
+<body>
+    <div id="container">
+        <form action="/search" method="post">
+            <!-- 跨站身份伪造: 利用浏览器存储的cookie中的用户身份标识冒充用户执行操作 -->
+            <!-- 防范跨站身份伪造最佳的做法就是在表单中放置随机令牌 -->
+            <!-- 除此之外通过设置令牌还可以防范表单重复提交以及重放攻击 -->
+            <!-- 隐藏域 / 隐式表单域: 页面上是无法看到该内容-->
+            {% csrf_token %}
+            <input type="search" name="carno" placeholder="请输入你的车牌号" required>
+            <input type="submit" value="搜索">
+        </form>
+        <hr>
+        {% if show_result %}
+        <table>
+            <tr>
+                <th>车牌号</th>
+                <th>违章原因</th>
+                <th>违章时间</th>
+                <th>处罚方式</th>
+                <th>是否受理</th>
+            </tr>
+            {% for record in record_list %}
+            <tr>
+                <td>{{ record.carno }}</td>
+                <td>{{ record.reason }}</td>
+                <td>{{ record.happen_date }}</td>
+                <td>{{ record.punish }}</td>
+                <td>
+                    {% if record.isdone %}
+                        <img src="{% static '/images/icon-yes.svg' %}">
+                    {% else %}
+                        <img src="{% static '/images/icon-no.svg' %}">
+                    {% endif %}
+                </td>
+            </tr>
+            {% endfor %}
+        </table>
+        {% endif %}
+    </div>
+</body>
+</html>

+ 110 - 0
Day31-Day35/car/templates/search2.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>车辆违章查询</title>
+    <style>
+        * {
+            font: 18px/30px Arial;
+        }
+        #container {
+            width: 960px;
+            margin: 0 auto;
+        }
+        #search {
+            width: 720px;
+            margin: 10px auto;
+            padding-top: 100px;
+        }
+        #search input[type=search] {
+            display: inline-block;
+            width: 480px;
+            height: 30px;
+        }
+        #search input[type=submit] {
+            display: inline-block;
+            width: 80px;
+            height: 40px;
+            border: None;
+            background-color: red;
+            color: white;
+            margin-left: 20px;
+        }
+        #result {
+            width: 920px;
+            margin: 20px auto;
+            border-collapse: collapse;
+        }
+        #result th {
+            font-weight: bolder;
+            border-bottom: 1px solid darkgray;
+        }
+        #result td, #result th {
+            text-align: center;
+            height: 50px;
+            width: 180px;
+        }
+    </style>
+</head>
+<body>
+    <div id="container">
+        <form id="search" action="/search" method="post">
+            <!-- 跨站身份伪造: 利用浏览器存储的cookie中的用户身份标识冒充用户执行操作 -->
+            <!-- 防范跨站身份伪造最佳的做法就是在表单中放置随机令牌 -->
+            <!-- 除此之外通过设置令牌还可以防范表单重复提交以及重放攻击 -->
+            <!-- 隐藏域 / 隐式表单域: 页面上是无法看到该内容-->
+            {% csrf_token %}
+            <input type="search" id="carno" name="carno" placeholder="请输入你的车牌号" required>
+            <input type="submit" value="搜索">
+            <a href="/add">添加新记录</a>
+        </form>
+        <hr>
+        <table id="result">
+            <thead>
+                <tr>
+                    <th>车牌号</th>
+                    <th>违章原因</th>
+                    <th>违章时间</th>
+                    <th>处罚方式</th>
+                    <th>是否受理</th>
+                </tr>
+            </thead>
+            <tbody>
+
+            </tbody>
+        </table>
+    </div>
+    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
+    <script>
+        $(function() {
+            $('#search').on('submit', function(evt) {
+                evt.preventDefault();
+                var carno = $('#carno').val();
+                var token = $('#search input[type=hidden]').val()
+                $.ajax({
+                    url: '/search2',
+                    type: 'post',
+                    data: {
+                        'carno': carno,
+                        'csrfmiddlewaretoken': token
+                    },
+                    dataType: 'json',
+                    success: function(json) {
+                        $('#result tbody').children().remove();
+                        for (var i = 0; i < json.length; i += 1) {
+                            var record = json[i];
+                            var tr = $('<tr>').append($('<td>').text(record.carno))
+                                .append($('<td>').text(record.reason))
+                                .append($('<td>').text(record.date))
+                                .append($('<td>').text(record.punish));
+                            var imgName = record.isdone ? 'icon-yes.svg' : 'icon-no.svg';
+                            tr.append($('<td>').append($('<img>').attr('src', '/static/images/' + imgName)));
+                            $('#result tbody').append(tr);
+                        }
+                    }
+                });
+            });
+        });
+    </script>
+</body>
+</html>

+ 41 - 0
Day31-Day35/oa/hrs/migrations/0001_initial.py

@@ -0,0 +1,41 @@
+# Generated by Django 2.0.5 on 2018-05-22 03:07
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Dept',
+            fields=[
+                ('no', models.IntegerField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=20)),
+                ('location', models.CharField(max_length=10)),
+            ],
+            options={
+                'db_table': 'tb_dept',
+            },
+        ),
+        migrations.CreateModel(
+            name='Emp',
+            fields=[
+                ('no', models.IntegerField(primary_key=True, serialize=False)),
+                ('name', models.CharField(max_length=20)),
+                ('job', models.CharField(max_length=10)),
+                ('mgr', models.IntegerField(null=True)),
+                ('sal', models.DecimalField(decimal_places=2, max_digits=7)),
+                ('comm', models.DecimalField(decimal_places=2, max_digits=7, null=True)),
+                ('dept', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='hrs.Dept')),
+            ],
+            options={
+                'db_table': 'tb_emp',
+            },
+        ),
+    ]

+ 43 - 0
Day31-Day35/oa/hrs/migrations/0002_auto_20180523_0923.py

@@ -0,0 +1,43 @@
+# Generated by Django 2.0.5 on 2018-05-23 01:23
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hrs', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='dept',
+            name='excellent',
+            field=models.BooleanField(default=0, verbose_name='是否优秀'),
+        ),
+        migrations.AlterField(
+            model_name='dept',
+            name='location',
+            field=models.CharField(max_length=10, verbose_name='部门所在地'),
+        ),
+        migrations.AlterField(
+            model_name='dept',
+            name='name',
+            field=models.CharField(max_length=20, verbose_name='部门名称'),
+        ),
+        migrations.AlterField(
+            model_name='dept',
+            name='no',
+            field=models.IntegerField(primary_key=True, serialize=False, verbose_name='部门编号'),
+        ),
+        migrations.AlterField(
+            model_name='emp',
+            name='comm',
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True),
+        ),
+        migrations.AlterField(
+            model_name='emp',
+            name='mgr',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+    ]

+ 19 - 0
Day31-Day35/oa/hrs/migrations/0003_auto_20180524_1646.py

@@ -0,0 +1,19 @@
+# Generated by Django 2.0.5 on 2018-05-24 08:46
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hrs', '0002_auto_20180523_0923'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='emp',
+            name='mgr',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hrs.Emp'),
+        ),
+    ]

+ 2 - 1
Day31-Day35/oa/hrs/models.py

@@ -24,7 +24,8 @@ class Emp(models.Model):
     no = models.IntegerField(primary_key=True)
     name = models.CharField(max_length=20)
     job = models.CharField(max_length=10)
-    mgr = models.IntegerField(null=True, blank=True)
+    mgr = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL)
+    # mgr = models.IntegerField(null=True, blank=True)
     sal = models.DecimalField(max_digits=7, decimal_places=2)
     comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
     dept = models.ForeignKey(Dept, on_delete=models.PROTECT)

+ 3 - 2
Day31-Day35/oa/hrs/urls.py

@@ -4,6 +4,7 @@ from hrs import views
 
 urlpatterns = [
     path('depts', views.depts, name='depts'),
-    path('depts/emps', views.emps, name='empsindept'),
-    path('deldepts', views.del_dept, name='ddel')
+    # url('depts/emps/(?P<no>[0-9]+)', views.emps, name='empsindept'),
+    path('depts/emps/<int:no>', views.emps, name='empsindept'),
+    path('deldept/<int:no>', views.del_dept, name='ddel')
 ]

+ 17 - 6
Day31-Day35/oa/hrs/views.py

@@ -1,5 +1,8 @@
+from django.http import HttpResponse
 from django.shortcuts import render, redirect
-from django.urls import reverse
+from django.db.models import ObjectDoesNotExist
+
+from json import dumps
 
 from hrs.models import Dept, Emp
 
@@ -11,13 +14,21 @@ def index(request):
     return render(request, 'index.html', context=ctx)
 
 
-def del_dept(request):
-    # 重定向 - 重新请求一个指定的页面
-    return redirect(reverse('depts'))
+def del_dept(request, no='0'):
+    try:
+        Dept.objects.get(pk=no).delete()
+        ctx = {'code': 200}
+    except (ObjectDoesNotExist, ValueError):
+        ctx = {'code': 404}
+    return HttpResponse(
+        dumps(ctx), content_type='application/json; charset=utf-8')
+    # 重定向 - 给浏览器一个URL, 让浏览器重新请求指定的页面
+    # return redirect(reverse('depts'))
+    # return depts(request)
 
 
-def emps(request):
-    no = request.GET['no']
+def emps(request, no='0'):
+    # no = request.GET['no']
     # dept = Dept.objects.get(no=no)
     # ForeignKey(Dept, on_delete=models.PROTECT, related_name='emps')
     # dept.emps.all()

+ 20 - 2
Day31-Day35/oa/templates/dept.html

@@ -5,6 +5,11 @@
     <meta charset="UTF-8">
     <title>部门</title>
     <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
+    <style>
+        #dept td, #dept th {
+            text-align: center;
+        }
+    </style>
 </head>
 <body>
 <div class="container">
@@ -14,6 +19,7 @@
             <hr>
 		</div>
 	</div>
+
     <div class="row clearfix">
         <div class="col-md-8 column">
             <table id="dept" class="table table-striped table-hover">
@@ -32,7 +38,7 @@
                         <td>{{ dept.no }}</td>
                         <td>
                             <!-- 写代码时要尽量避免使用硬编码(hard code) -->
-                            <a href="{% url 'empsindept' %}?no={{ dept.no }}">{{ dept.name }}</a>
+                            <a href="{% url 'empsindept' dept.no %}">{{ dept.name }}</a>
                         </td>
                         <td>{{ dept.location }}</td>
                         <td>
@@ -43,7 +49,7 @@
                             {% endif %}
                         </td>
                         <td>
-                            <a href="{% url 'ddel' %}?dno={{ dept.no }}" class="btn btn-xs btn-warning">删除</a>
+                            <a id="{{ dept.no }}" href="javascript:void(0);" class="btn btn-xs btn-warning">删除</a>
                         </td>
                     </tr>
                     {% endfor %}
@@ -60,6 +66,18 @@
     $(function() {
         $('#dept tbody tr:even').addClass('info');
         $('#dept tbody tr:odd').addClass('warning');
+        $('#dept a[id]').on('click', function(evt) {
+            var a = $(evt.target);
+            if (confirm('确定要删除吗?')) {
+                $.getJSON('/hrs/deldept/' + a.attr('id'), function(json) {
+                    if (json.code == 200) {
+                        a.parent().parent().remove();
+                        $('#dept tbody tr:even').addClass('info');
+                        $('#dept tbody tr:odd').addClass('warning');
+                    }
+                });
+            }
+        });
     });
 </script>
 </body>