豪翔天下

Change My World by Program

0%

Django Admin 后台管理系统

Django自带了强大的名为admin的后台管理功能,app名称为django.contrib.admin,它同时依赖了django.contrib.auth认证系统和django.contrib.sessions系统,当然,即使不用admin,后面两者都建议加上,不用重复造轮子。

  • 为了使用它,我们需要先使用migrate功能去创建相应的数据库表,直接执行python manage.py makemigrations && python manage.py migrate即可。运行程序后,直接访问http://127.0.0.1:8000/admin/就能访问admin了(一般admin的路由都是定义好了的,在urls.py中有 url(r'^admin/', admin.site.urls),)

  • 我们需要先创建一个超级管理员python manage.py createsuperuser,按照提示输入用户名密码即可用来登录了

  • 如果要让字段非必填,需要在定义model字段的时候就加上blank=True参数

  • 修改超级管理员密码可以这样做:

    1
    2
    3
    4
    5
    # python manage.py shell
    from django.contrib.auth.models import User
    user =User.objects.get(username='admin')
    user.set_password('new_password')
    user.save()

使用admin管理数据表

为了管理具体的某张表,我们需要在app下的admin.py文件里面注册相应的model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from myapp.models import PostModel

class PostAdmin(admin.ModelAdmin):
class Media:
js = ["js/test.js"] # 可以引入自定义的js实现更多的个性化功能(这里该js文件位于static/js/test.js下)
css = ["css/test.css"] # 自定义css

list_display = ('id', 'name', 'created_at') # 定义后台列表显示时需要显示哪些字段
list_per_page = 50 # 定义后台列表显示时每页显示数量
list_filter = ('id', 'name', ) # 定义后台列表显示时能够筛选的字段(会列出所有的可筛选值)
search_fields = ('username', 'email',) # 指定能搜索哪些字段
list_editable = ['field'] # 定义可以直接在列表页进行更改的字段
fx_fields = ('user_id', ) # 定义列表页显示的外键字段,会直接显示关联的值
filter_horizontal = ('posts') # 显示多对多字段
readonly_fields = ('username') # 设置只读字段,不允许更改
ordering = ('-created_at', ) # 定义列表显示的顺序,负号表示降序
fieldsets = ( # 对字段进行分类/分组设置,前端会分开显示
(None, {'fields': ('username', 'password', )}),
('Personal info', {'fields': ('firstname', )}),
('Permissions', {'fields': ('groups', )}),
)
add_fieldsets = ( # 定义添加数据时需要填写哪些字段
(None, {'classes': ('wide',), 'fields': ('name', 'content' )}),
)

def save_model(self, request, obj, form, change):
"""定义保存对象的方式,可以在这里做一些其他的事情。这个方法不需要返回任何东西,如果要捕获其中的错误或者重定向,最好是设置一个对象字段,然后在response_change或response_add中进行处理"""
self.has_error = True
self.message_user(request, "友好地给用户显示错误信息", level=logging.ERROR)
super().save_model(request, obj, form, change)
return

def response_change(self, request, obj):
"""改变记录之后的响应操作,可以捕获错误并返回应该返回的消息"""
if self.has_error:
return HttpResponseRedirect(request.path)
else:
return super().response_chage(request, obj)

def respopnse_add(self, request, obj, post_url_continue=None):
"""添加记录之后的响应操作"""
return super().response_add(request, obj, post_url_continue)

admin.site.register(PostModel, PostAdmin) # 注册管理模型
admin.site.site_header = '后台页面的header'
admin.site.site_title = '后台的title'

@admin.register(UserModel) # 也可以用装饰器直接进行注册
class UserAdmin(admin.ModelAdmin):
pass

自定义admin管理数据创建及修改表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyModelCreationForm(forms.ModelForm):
"""自定义创建表单"""
field_name = forms.CharField(label='field_name')

class Meta:
model = MyModel
fields = ('email', 'field2')
exclude = ('created_at', ) # 排除某些字段不用在后台管理,隐藏字段

class MyModelChangeForm(forms.ModelForm):
"""自定义修改表单"""
password = ReadOnlyPasswordHashField(label= ("Password"), help_text= ("Raw passwords are not stored, so there is no way to see this user's password, but you can change the password using <a href=\"../password/\">this form</a>.")) # 如果要修改密码字段,我们需要这样提示
def clean_password(self):
return self.initial["password"]

class UserAdmin(BaseUserAdmin): # 用户管理需要继承单独的Admin
form = UserChangeForm # 指定修改表单
add_form = UserCreationForm # 指定创建表单,如果不指定默认都会使用form

def get_form(self, request, obj=None, **kwargs):
"""如果仅仅是某几个字段的简单修改可以直接这样写,不用定义新的Form类"""
kwargs['widgets'] = {
'name': forms.TextInput(attrs={'placeholder': 'this is a example'})
}
return super().get_form(request, obj, **kwargs)

admin中字段动态hide与show

  • 目前没有找到更好的方法,不过可以直接用class Media: js=()来自定义js文件,通过jQuery来实现

自定义字段的样式及js逻辑

  • 如果要发送ajax请求,获取csrf_token放在header里,可以通过$('input[name=csrf_token]').val()获取

Django-CKeditor富文本编辑使用

django-ckeditor扩展,使用简单,前端也漂亮

  1. 安装pip install django-ckeditor,如果要在富文本里添加图片还需要pip install pillow

  2. 注册应用,INSTALLED_APPS里添加ckeditor,图片还需要添加ckeditor_uploader

  3. 如果要使用自定义的文件存储,例如七牛云存储,可以参考这篇文章进行设置

  4. 如果要处理图片,还需要在settings.py里面添加如下设置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 前面两个可能已经设置了,是存放用户上传文件的地方
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

    CKEDITOR_UPLOAD_PATH = 'upload/'

    CKEDITOR_CONFIGS = { # 添加个性化的配置
    'default': {
    'image_previewText':' ', # 替换图片显示区域那一串搞不懂的字符串
    'tabSpaces': 4,
    }
    }

    另外还需要添加一个路由用于上传请求

    1
    2
    3
    4
    5
    6
    from django.conf.urls.static import static

    urlpatterns = [
    ...
    path('ckeditor/', include('ckeditor_uploader.urls')),
    ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

    模型里面对应的字段设置

    1
    2
    3
    4
    5
    from ckeditor.fields import RichTextField

    class Post(models.Model):
    content = RichTextField()
    content2 = RichTextUploadingField() # 带有上传图片功能的富文本编辑

    Django-CKeditor目前还不支持粘贴的时候自动上传图片,我的做法是在对象的Adminsave_model方法里面对没有转换为内链的外部链接图片通过requests抓取下来再上传到七牛云,以达到自动替换的效果。这只是最简单的做法,当然,最好的做法还是在前端监听粘贴事件,实时上传。

    Django-MarkdownX文本编辑器使用

    目前,Django-CKeditor暂时还不支持markdown,所以只能用Django-Markdownx来代替,要兼容两者就只能用两个字段来存储了。引入步骤:

    1. 安装依赖,并且将markdownx加入INSTALLED_APPS

      1
      pip install django-markdownx
    2. 添加路由vim app/urls.py:

      1
      2
      3
      4
      urlpatterns = [
      [...]
      url(r'^markdownx/', include('markdownx.urls')),
      ]
    3. 字段定义

      1
      2
      class Post(models.Model):
      content = MarkdownxField()
    4. 自定义tag,vim app/templatetags/base.py

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      import markdown
      from django import template
      from django.utils.safestring import mark_safe

      register = template.Library()

      @register.filter(is_safe=True)
      def custom_markdown(value):
      return mark_safe(
      markdown.markdown(
      value,
      extensions=[
      "markdown.extensions.extra",
      "markdown.extensions.toc",
      "markdown.extensions.sane_lists",
      "markdown.extensions.nl2br",
      "markdown.extensions.codehilite",
      ],
      safe_mode=True,
      enable_attributes=False,
      )
      )

      @register.filter(is_safe=False)
      def custom_markdown_unsafe(value):
      return mark_safe(
      markdown.markdown(
      value,
      extensions=[
      "markdown.extensions.extra",
      "markdown.extensions.toc",
      "markdown.extensions.sane_lists",
      "markdown.extensions.nl2br",
      "markdown.extensions.codehilite",
      ],
      safe_mode=False,
      enable_attributes=False,
      )
      )
    5. 添加相关配置到配置文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      MARKDOWNX_MARKDOWNIFY_FUNCTION = 'markdownx.utils.markdownify'
      MARKDOWNX_MARKDOWN_EXTENSION_CONFIGS = {}
      MARKDOWNX_URLS_PATH = '/markdownx/markdownify/'
      MARKDOWNX_UPLOAD_URLS_PATH = '/markdownx/upload/'
      MARKDOWNX_MEDIA_PATH = 'media/markdownx/img'
      MARKDOWNX_UPLOAD_MAX_SIZE = 5 * 1024 * 1024
      MARKDOWNX_UPLOAD_CONTENT_TYPES = ['image/jpeg', 'image/png', 'image/svg+xml']
      MARKDOWNX_IMAGE_MAX_SIZE = {'size': (800, 500), 'quality': 100,}
      MARKDOWNX_EDITOR_RESIZABLE = True
      MARKDOWNX_MARKDOWN_EXTENSIONS = [
      'markdown.extensions.fenced_code',
      'markdown.extensions.codehilite',
      'markdown.extensions.smarty',
      'markdown.extensions.extra',
      'markdown.extensions.tables',
      'markdown.extensions.sane_lists',
      ]
  5. 前端模板这样渲染

    1
    2
    3
    <div>
    {{ content | custom_markdown }}
    </div>
    左右分割显示

    默认的编辑器样式是上下分割,上面编辑,下面实时显示,但是对于长文本十分不方便,如果要进行左右分割,需要这样做:

    1. 引入bootstrap依赖,并且将bootstrap3加入INSTALLED_APPS

      1
      pip install django-bootstrap3
    2. 覆写原本的编辑框,app/templates/markdownx/widget2.html:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {% load bootstrap3 %}
      {% bootstrap_css %}
      {% bootstrap_javascript %}
      <div class="markdownx row">
      <div class="col-md-6">
      {% include 'django/forms/widgets/textarea.html' %}
      </div>
      <div class="col-md-1"></div>
      <div class="col-md-5">
      <div class="markdownx-preview"></div>
      </div>
      </div>
    3. 添加admin管理:

      1
      2
      3
      4
      class PostAdmin(admin.ModelAdmin):
      formfield_overrides = {
      models.TextField: {'widget': AdminMarkdownxWidget},
      }

    TroubleShooting

    • Python crashes after trying to visit admin page with exit code 245: 每次进入admin页面程序就崩溃,可以尝试更新python版本或者更新django版本,我3.7.0b2遇到过这个问题,升级Python得以解决

    • 隐藏或修改Admin中的第三方APP管理: 随便找个自己的app,在admin.py中取消该app的注册:

      1
      2
      3
      4
      5
      6
      admin.site.unregister(ThirdModel)

      #而如果要修改第三方的管理类,可以这样做,重新注册一个新的类
      class NewThirdModel(ThirdModel):
      pass
      admin.site.register(ThirdModel, NewThirdModel)

推荐阅读

坚持原创技术分享,谢谢支持

欢迎关注我的其它发布渠道