Flash video website (background management)

brief introduction

  • This part should realize the specific background management logic
  • The basic logic is as follows:

Administrator login

  • Move the authentication part of the database in the previous models to the app initialization file
  • Most of the content in this section is based on the front page, which is why the previous section built the page first
  • Flash is used for submitting and verifying all forms in flash_ WTF, you can install it first
    • Activate the virtual environment, PIP install flash WTF
    • This extension defines many fields and validators to be used in the form, such as string, password, submission, etc., which also belong to the model
    • Similarly, in my foundation note There is a more detailed explanation in
  • In forms.py
    from flask_sqlalchemy import SQLAlchemy
    from flask import Flask
    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, SubmitField
    from wtforms.validators import DataRequired, ValidationError
    
    
    class LoginForm(FlaskForm):
        """Administrator login form"""
        account = StringField(
            label='account number',
            validators=[
                DataRequired('Please enter the account number!')
            ],
            description='account number',
            render_kw={
                "class":"from-control",
                "placeholder":"Please enter the account number",
                "required":"required"
            }
        )
        pwd = StringField(
            label='password',
            validators=[
                DataRequired('Please enter the account number!')
            ],
            description='account number',
            render_kw={
                "class": "from-control",
                "placeholder": "Please enter the account number",
                "required": "required"
            }   # The attributes passed to the front-end tag are copied directly from the template
        )
    
        submit = SubmitField(
            label='Sign in',
            render_kw={
                "class": "btn btn-primary btn-block btn-flat",
            }
        )
    
  • Render model to template
    • The operation of the template should go through the view in views.py
    from app.admin.forms import LoginForm
    
    
    # Background login
    @admin.route('/login/', methods=["GET", "POST"])
    def login():
        form = LoginForm()
        # In turn, execute judgment
        if form.validate_on_submit():
            data = form.data
            admin = Admin.query.filter_by(name=data['account']).first() # This is an object that contains information
            if not admin.check_pwd(data['pwd']):
                flash("Wrong password!")
                return redirect(url_for('admin.login'))
            # Correct password, save account
            session['admin'] = data['account']  # account number
            return redirect(request.args.get('next') or url_for('admin.index'))
        return render_template('admin/login.html', form=form)
    
    • Replace in the template, login.html
    <input name="user" type="text" class="form-control" placeholder="Please enter the account number!">
    {{ form.account }}
    <input name="pwd" type="password" class="form-control" placeholder="Please input a password!">
    {{ form.pwd }}
    <a id="btn-sub" type="submit" class="btn btn-primary btn-block btn-flat">Sign in</a>
    {{ form.submit }}
    
  • The csrf field will be reported here. This is a protection policy (Cross Site Request Forgery). We can check the official file use
    • We configure a secret for the app_ Key, which can be generated using the uuid module
    • Then just add {{form.csrf_token}} to the template to generate a hidden csrf tag
  • Now that there is validation, how can I prompt the error message when validating in the template? Modify under each form field:
    {% for err in form.account.errors %}
    <div class="col-md-12">
        <font style="color:red">{{err}}</font>
    </div>
    {% endfor %}
    
    • This doesn't work. It's harmless. Let's see later!
    • OK, this is not invalid, but the account password is entered before verification and error message is displayed!
  • The results of form validation and the received data are processed in the view (before - > after)
    • Of course, this verification method is also managed in the form model
    def validate_account(self, field):
        account = field.data
        admin = Admin.query.filter_by(name=account).count() # Query user information using database model
        if admin == 0:
            raise ValidationError("Account does not exist!")
    
    • The password is hash ed. We define the verification method in the model class of admin
    # models.py
    # class Admin
    def check_pwd(self, pwd):
        from werkzeug.security import check_password_hash
        return check_password_hash(self.pwd, pwd)
    
    • Then define the verification password in the view (the account already exists and pass the form data to the view)
    from flask import Flask, render_template, redirect, url_for, flash, session, request
    # Background login
    @admin.route('/login/', methods=["GET", "POST"])
    def login():
        form = LoginForm()
        if form.validate_on_submit():
            data = form.data
            admin = Admin.query.filter_by(name=data['account']).first() # This is an object that contains information
            if not admin.check_pwd(data['pwd']):
                flash("Wrong password!")
                return redirect(url_for('admin.login'))
            # Correct password, save account
            session['admin'] = data['account']  # account number
            return redirect(request.args.get('next') or url_for('admin.index'))
        return render_template('admin/login.html', form=form)
    
    • The message flash is used here. Something needs to be added to the front end
    {% for msg in get_flashed_messages() %}
    <p class="login-box-msg" style="color:red;">{{ msg }}</p>
    {% endfor %}
    
  • Many pages need to be logged in to access. Use the decorator to limit the view function
    # admin/views.py
    from functools import wraps	# The function is not to change the information of the decorated function
    
    def admin_login(f):
    @wraps(f)
    def inner(*args, **kwargs):
        if "admin" not in session:	# session['admin'] is None cannot be used
            return redirect(url_for('admin.login', next=request.url))	# The next parameter indicates the address requested before continuing
        return f(*args, **kwargs)	# Return, do not call
    return inner	
    
    # Then we add this decorator syntax sugar to each view function, such as
    @admin.route('/logout')
    @admin_login
    def logout():
        session.pop('admin', None)  # Clear session
        return redirect(url_for('admin.login'))
    # Note that in the admin.html layout file, exit is routed to logout
    
  • session will save the user name and password, and clear them after logout!
    • What is the difference between session and cookie?

Label management

  • Manage the logic of administrator login. The first four steps are common!
    • Define a form model in forms
    • Pass in view
    • Render in template
    • Add csrf authentication
    • Define a verifier in the form model to determine whether the user exists
    • Define password verification methods in the database model
    • Restrict page access using decorators
  • I'm posting logic here
    class TagForm(FlaskForm):
        """Label add form"""
        name = StringField(
            label='Label name',
            validators=[
                DataRequired("Please enter the label name!")
            ],
            description="label",
            render_kw={
                "class" : "form-control",
                "id" : "input_name",
                "placeholder" : "Please enter the label name!"
            }
        )
        submit = SubmitField(
            label="add to",
            render_kw={
                "class" : "btn btn-primary"
            }
        )
    
  • Define warehousing method
    # Addition and list of labels
    @admin.route('/tag/add', methods=["GET", "POST"])   # This method must be defined, otherwise the validator will not be displayed
    @admin_login
    def tag_add():
        form = TagForm()
        print("aaaaaaaaaaaaaaaa")
        if form.validate_on_submit():   # No verification
            data = form.data
            tag = Tag.query.filter_by(name=data['name']).count()
            if tag == 1:
                flash("Label already exists!", "err")
                return redirect(url_for('admin.tag_add'))
            tag = Tag(
                name= data['name']
            )
            db.session.add(tag)
            db.session.commit()
            flash("Tag added successfully", 'ok')   # The second parameter is a fixed number of values that are applied to the template category_filter=["ok"]
        return render_template('admin/tagadd.html', form=form)
    
    • I've made a mistake here because of {{form.csrf_token}} for a long time. Be careful!
  • Find a template prompt at the front end
    {% for msg in get_flashed_messages(category_filter=["ok"]) %}
    <div class="alert alert-success alert-dismissible">
         <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
         <h4><i class="icon fa fa-check"></i> Operation succeeded</h4>
         {{ msg }}
     </div>
     {% endfor %}
    
     {% for msg in get_flashed_messages(category_filter=["err"]) %}
     <div class="alert alert-danger alert-dismissible">
         <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
         <h4><i class="icon fa fa-ban"></i> operation failed</h4>
         {{ msg }}
     </div>
     {% endfor %}
     <div class="form-group">
    
  • Then, it displays and processes the editing and deletion operations in the tab list
    • Paging display uses regular matching in routing: < int: Page >, and the front end needs to pass the page number!
    @admin.route('/tag/list/<int:page>/', methods=['GET'])
    @admin_login
    def tag_list(page=None):
        if page is None:
            page = 1
        page_data = Tag.query.order_by(
            Tag.addtime.desc()
        ).paginate(page=page, per_page=2)
        return render_template('admin/taglist.html', page_data=page_data)
    
    • The extension SQLAlchemy is also used to process paging, and a new UI / admin is created in templates_ Page.html, using macro syntax, can refer to the official documentation
    • Note that the way the front end passes parameters here is still in the url_ Using / splicing in for, automatically putting in url and regular parsing, which is also different from query parameters? x=x
    • For example: {{url_for('admin.tag_edit', id=v.id)}}
    # menu.html, initialize the first page
    <li>
        <a href="{{url_for('admin.tag_list', page=1)}}">
            <i class="fa fa-circle-o"></i> Tag list
        </a>
    </li>
    
    <!--ui/admin_page.html-->
    {% macro page(data,url) -%}
    {% if data %}
    <ul class="pagination pagination-sm no-margin pull-right">
        <li><a href="{{url_for(url,page=1)}}">home page</a></li>
    
        {% if data.has_prev %}
        <li><a href="{{url_for(url,page=data.prev_num)}}">previous page</a></li>
        {% else %}
        <li class="disabled"><a href="">previous page</a></li>
        {% endif %}
    
        {% for v in data.iter_pages() %}
            {% if v==data.page %}
            <li class="active"><a href="#">{{v}}</a></li>
            {% else %}
            <li><a href="{{url_for(url, page=v)}}">{{v}}</a></li>
            {% endif %}
        {% endfor %}
    
        {% if data.has_next %}
        <li><a href="{{url_for(url,page=data.next_num)}}">next page</a></li>
        {% else %}
        <li class="disabled"><a href="">previous page</a></li>
        {% endif %}
    
        <li><a href="{{url_for(url, page=data.pages)}}">Last page</a></li>
    </ul>
    {% endif %}
    {% endmacro %}
    
    <!--taglist.html-->
    {% import "ui/admin_page.html" as pg%}
    <div class="box-footer clearfix">
    	<!--call macro function-->
        {{pg.page(page_data, 'admin.tag_list')}}
    </div>
    
  • List deletion and editing
    • Similarly, editing and adding involve form submission. In the view, the form is rendered to the front end, submitted, and then returned for verification and warehousing
    • If it is deleted, you only need to pass parameters (splicing url, regular capture) and modify the database
    • If it is a query, you only need to query the database and page
    • Additions, deletions and changes are all involved here! The rule is parameter transfer + view - front end - view; There are also query parameters that are not involved
      • This parameter may be passed first or later. To delete here is to pass the id parameter first. If the list is the page to pass the request later
    # Label deletion
    @admin.route('/tag/del/<int:id>/', methods=['GET'])
    @admin_login
    def tag_del(id=None):
        tag = Tag.query.filter_by(id=id).first_or_404() # If it is not found, an error will be reported
        db.session.delete(tag)
        db.session.commit()
        flash("Delete succeeded!", "ok")
        return redirect(url_for('admin.tag_list', page=1))
    # html
    <a href="{{url_for('admin.tag_del', id=v.id)}}" class="label label-danger">delete</a>
    
    # Label editing
    @admin.route('/tag/edit/<int:id>/', methods=['GET', 'POST'])
    @admin_login
    def tag_edit(id=None):
        # Editing is equivalent to resubmitting a form
        form = TagForm()	# Share form model with tagadd
        tag = Tag.query.get_or_404(id)  # Label name to modify
        
        if form.validate_on_submit():
            data = form.data    # Submitted tag name
            tag_count = Tag.query.filter_by(name=data['name']).count()
            if tag_count == 1:  # tag.name != data['name'] and
                flash("Name already exists", 'err')
            tag.name = data['name']
            db.session.add(tag)
            db.session.commit()
            flash("Edit succeeded!", "ok")
            return redirect(url_for('admin.tag_list', page=1))
        
        return render_template("admin/tagedit.html", form=form, tag=tag)	# Render to the front end before returning to validation
    
  • To create a new editing page, copy tagadd.html. Note the method of using tag variable: {{form.name(value=tag.name)}}
    {% extends "admin/admin.html" %}
    
    {% block content %}
    <section class="content-header">
        <h1>Micro film management system</h1>
        <ol class="breadcrumb">
            <li><a href="#"> < I class =" Fa FA dashboard "> < / I > label management</a></li>
            <li class="active">Edit label</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <h3 class="box-title">Edit label</h3>
                    </div>
                    <form role="form" method="post">
                        <div class="box-body">
                            <!--Find a box indicating success-->
                            {% for msg in get_flashed_messages(category_filter=["ok"]) %}
                            <div class="alert alert-success alert-dismissible">
                                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                                <h4><i class="icon fa fa-check"></i> Operation succeeded</h4>
                                {{ msg }}
                            </div>
                            {% endfor %}
    
                            {% for msg in get_flashed_messages(category_filter=["err"]) %}
                            <div class="alert alert-danger alert-dismissible">
                                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                                <h4><i class="icon fa fa-ban"></i> operation failed</h4>
                                {{ msg }}
                            </div>
                            {% endfor %}
                            <div class="form-group">
                                <label for="input_name">{{form.name.label}}</label>
                                {{form.name(value=tag.name)}}
                                {% for err in form.name.errors %}
                                    <div class="col-md-12">
                                        <span style="color: red">{{ err }}</span>
                                    </div>
                                {% endfor %}
                                <!--<input type="text" class="form-control" id="input_name" placeholder="Please enter the label name!">-->
                            </div>
                        </div>
                        <div class="box-footer">
                            {{form.submit}}
                            {{form.csrf_token}}
                            <!--<button type="submit" class="btn btn-primary">add to</button>-->
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>
    {% endblock %}
    
    {% block js%}
    <script>
        $(document).ready(function () {
            $('#m-2').addClass("active");
        })
    </script>
    {% endblock %}
    
  • The form model is not modified. It is uniformly called edit (add / modify)

Film management

  • Around the core processing idea of "parameter transfer + look ahead", we began the backstage film management
  • Add movie
    • To render a form from the view, first define the form model according to the database model
      class MovieForm(FlaskForm):
          """Movie add form"""
          title = StringField(
              label='Movie title',
              validators=[
                  DataRequired("Please enter the movie name!")
              ],
              description="label",
              render_kw={
                  "class": "form-control",
                  "id": "input_title",
                  "placeholder": "Please enter the title!"
              }
          )
      
          url = FileField(
              label='file',
              validators=[
                  DataRequired("Please upload the file")
              ],
              description="file"
          )
      
          info = TextAreaField(
              label='brief introduction',
              validators=[
                  DataRequired("Please enter a movie profile")
              ],
              description="brief introduction",
              render_kw={
                  "class": "form-control",
                  "rows":"10",
                  "id": "input_info"
              }
          )
      
          logo = FileField(
              label='logo picture',
              validators=[
                  DataRequired("Please upload the file")
              ],
              description="logo"
          )
      
          star = SelectField(
              label='Star',
              validators=[
                  DataRequired("Please select a star")
              ],
              description="Star",
              coerce=int,
              choices=[(1,"1 Star"),(2,"2 Star"),(3,"3 Star"),(4,"4 Star"),(5,"5 Star")],
              render_kw={
                  "class": "form-control",
                  "id": "input_star"
              }
          )
      
          tag_id = SelectField(
              label='label',
              validators=[
                  DataRequired("Please select a label")
              ],
              description="label",
              coerce=int,
              choices=[(v.id, v.name) for v in tags], # List derivation. Only v.name is displayed in the front end and v.id is displayed in the view
              render_kw={
                  "class": "form-control",
                  "id": "input_tag_id"
              }
          )
      
          area = StringField(
              label='region',
              validators=[
                  DataRequired("Please enter region")
              ],
              description="region",
              render_kw={
                  "class": "form-control",
                  "id": "input_area",
                  "placeholder" : "Please enter the region!"
              }
          )
      
          length = StringField(
              label='Film length',
              validators=[
                  DataRequired("Please enter the film length")
              ],
              description="region",
              render_kw={
                  "class": "form-control",
                  "id": "input_length",
                  "placeholder" : "Please enter the film length!"
              }
          )
      
          release = StringField(
              label='Release time',
              validators=[
                  DataRequired("Please enter the release time")
              ],
              description="Release time",
              render_kw={
                  "class": "form-control",
                  "id": "input_release_time",
                  "placeholder" : "Please enter the release time!"
              }
          )
      
          submit = SubmitField(
              label="add to",
              render_kw={
                  "class": "btn btn-primary"
              }
          )
      
    • Import the database and form model in the view and prepare to the front end
      <!--In terms of process, we should not forget this sentence, limit method-->
      <form role="form" method="POST" enctype="multipart/form-data">
      
    • After the front-end POST, go back to the view verification and pay attention to the saving of the file
      # Adding movies
      from werkzeug.utils import secure_filename
      
      @admin.route('/movie/add', methods=['GET','POST'])
      @admin_login
      def movie_add():
          form = MovieForm()
          if form.validate_on_submit():
              data = form.data
              # Prepare a data insert
              # Filter data name
              file_url = secure_filename(form.url.data.filename)
              file_logo = secure_filename(form.logo.data.filename)
              # Prepare storage path
              if not os.path.exists(app.config['UP_DIR']):
                  os.mkdir(app.config['UP_DIR'])
                  os.chmod(app.config['UP_DIR'], "rw")
              # Change file name
              file_url = changeFileName(file_url)
              file_logo = changeFileName(file_logo)
              # Save file
              form.url.data.save(app.config['UP_DIR'] + file_url)
              form.logo.data.save(app.config['UP_DIR'] + file_logo)
              
              movie = Movie(
                  title = data['title'],
                  url = file_url,
                  info = data["info"],
                  logo = file_logo,
                  star = int(data["star"]),
                  playnum = 0,
                  commentnum = 0,
                  tag_id = int(data['tag_id']),	# v.id is given to the view, which is related to the choices attribute of the form model
                  area = data['area'],
                  release_time = data['release'],
                  length = data["length"]
              )
              db.session.add(movie)
              db.session.commit()
              flash("Added movie successfully!", "ok")
          return render_template('admin/movieadd.html', form=form)
      
  • To upload files here, you need to__ init__ Configured and processed
    app.config['UP_DIR'] = os.path.join(os.path.abspath(__file__),"/static/uploads/") # Splice the save path of the uploaded file with the current file as the reference
    
  • Define a function in the view to change the name of the uploaded file
    from werkzeug.utils import secure_filename
    import os
    from datetime import datetime
    
    def changeFileName(filename):
        '''
        Modify the name of the incoming file
        :return:
        '''
        file = os.path.splitext(filename)   # Separate names and suffixes
        filename = datetime.now().strftime("%Y%m%d%H%M%S")+str(uuid.uuid4().hex)+str(file[-1])
        return filename
    
    • Video images are stored directly in the server folder, and paths are stored in the database
  • Here's a problem
    # Use the save method when saving a file
    form.url.data.save(app.config['UP_DIR'] + file_url)	# It's a plus sign, not a comma
    # When adding configuration in app
    app.config['UP_DIR'] = os.path.join(os.path.abspath(os.path.dirname(__file__)),"static/uploads/") # This / must be written after uploads
    
    # Data in current table
    delete from movie;	# truncate has foreign key constraints
    
  • Movie list
    • Update the menu bar link with the page=1 parameter
    • The process is the same. View (query paging data) - front end - view (return requested page number); Here, the view function only needs to support the GET method
    @admin.route('/movie/list/<int:page>', methods=['GET'])
    @admin_login
    def movie_list(page=None):
        if page==None:
            page = 1
        # One film after another
        page_data = Movie.query.join(Tag).filter(
            Tag.id == Movie.tag_id  # When associating multiple tables, use movie as multiple terminals. Here is the whole record corresponding to the tag table. Use v.tag.name when using
        ).order_by(
            Movie.addtime.desc()
        ).paginate(page=page, per_page=10)
        return render_template('admin/movielist.html', page_data=page_data)
    
  • Movie deletion, general operation
    @admin.route('/movie/del/<int:id>', methods=['GET'])
    @admin_login
    def movie_del(id):
        movie = Movie.query.get_or_404(int(id)) # If you don't jump directly to 404
        db.session.delete(movie)    # Comments and other data are associated with movie. Movie is the main table, so it will be deleted together
        db.session.commit()
        flash("Movie deleted successfully!", "ok")  # Lowercase ok
        return redirect(url_for("admin.movie_list", page=1))    # Redirection requires template rendering with add edit and list
    
    • add edit and list are required for template rendering, and only redirection is required for deletion
  • Movie Editing: parameters - View - front end - view (save)
    # Edit movie
    @admin.route('/movie/edit/<int:id>', methods=['GET', "POST"])
    @admin_login
    def movie_edit(id):
        form = MovieForm()
        form.url.validators = []
        # print(form.url.validators)
        form.logo.validators = []   # Skip validation of unselected files
        # Question: can't you get rid of the required field? It is found that there is no problem in printing. Solution:
        # render_kw = {
        #             "required": False
        #         }
        # However, you will not be prompted to select a file when adding
        # A better solution is to edit. HTML {{form. Logo (required = false)}}
    
        movie = Movie.query.get_or_404(int(id)) # If you don't jump directly to 404
        # Deal with the problem that some original information of movie is not displayed, and distinguish it by request method
        if request.method=="GET":
            form.info.data = movie.info
            form.star.data = movie.star
            form.tag_id.data = movie.tag_id # value=movie.info is not required in the template
    
        if form.validate_on_submit():   # POST
            data = form.data
            m = Movie.query.filter_by(title=data['title']).count()
            if m==1:
                flash("Movie already exists","err")
                return  redirect(url_for("admin.movie_edit", id=id))
            movie.title = data["title"]
            movie.info = data["info"]
            movie.star = data["star"]
            movie.tag_id = data["tag_id"]
            movie.area = data["area"]
            movie.length = data["length"]
            movie.release_time = data["release"]    # name attribute
    
            # File retransmission
            if not os.path.exists(app.config["UP_DIR"]):
                os.mkdir(app.config["UP_DIR"])
                os.chmod(app.config["UP_DIR"], "rw")
    
            if form.url.data.filename != "":    # Q: the selected file is empty. Now there is a value indicating retransmission (it has nothing to do with displaying the original file)
                file_url = secure_filename(form.url.data.filename)
                movie.url = changeFileName(file_url)    # Update original data information
                form.url.data.save(app.config["UP_DIR"] + movie.url)
            if form.logo.data.filename != "":
                file_logo = secure_filename(form.logo.data.filename)
                movie.logo = changeFileName(file_logo)
                form.logo.data.save(app.config["UP_DIR"] + movie.logo)
    
            db.session.add(movie)   # modify
            db.session.commit()
            flash("Modified successfully", "pk")
            return redirect(url_for('admin.movie_list', page=1))
    
        return render_template("admin/movieedit.html", form=form, movie=movie)  # Render the original information
    

Film preview management

  • Add and list, check the database and find that there are only two form model fields
  • View front view; View - paging (background data - foreground rendering); Parameter transfer; file save
    # Add and list of trailers
    @admin.route('/preview/add', methods=['GET', 'POST'])
    @admin_login
    def preview_add():
        '''The same process as adding movies'''
        form = PreviewForm()
        if form.validate_on_submit():
            # filter
            file_logo = secure_filename(form.logo.data.filename)
            # Prepare storage path
            if not os.path.exists(app.config["UP_DIR"]):
                os.mkdir(app.config["UP_DIR"])
                os.chmod(app.config["UP_DIR"], "rw")
            # Change file name
            file_logo = changeFileName(file_logo)
            # Save file
            form.logo.data.save(app.config["UP_DIR"] + file_logo)
    
            data = form.data
            # Prepare a data entry
            preview = Preview(
                title = data["title"],
                logo = file_logo    # Just save your name
            )
            db.session.add(preview)
            db.session.commit()
            flash("Successfully added Trailer!", "ok")
            return redirect(url_for('admin.preview_add'))
        return render_template('admin/previewadd.html', form=form)
    
    
    @admin.route('/preview/list/<int:page>')
    @admin_login
    def preview_list(page=None):
        if page==None:
            page = 1
        page_data = Preview.query.order_by(
            Preview.addtime.desc()
        ).paginate(page=page, per_page=10) # The view part provides data in the form of dictionary; The foreground part is rendered using macro
        return render_template('admin/previewlist.html', page_data=page_data)
    
    @admin.route('/preview/del/<int:id>')
    @admin_login
    def preview_del(id=None):
        preview = Preview.query.get_or_404(int(id))  # If you don't jump directly to 404
        db.session.delete(preview)  # Comments and other data are associated with movie. Movie is the main table, so it will be deleted together
        db.session.commit()
        flash("Preview deleted successfully!", "ok")  # Lowercase ok
        return redirect(url_for("admin.preview_list", page=1))  # Redirection, template rendering includes add edit and list
    
    @admin.route('/preview/edit/<int:id>', methods=['GET', 'POST'])
    @admin_login
    def preview_edit(id=None):
        form = PreviewForm()
        form.logo.validators = []
    
        preview = Preview.query.get_or_404(int(id))  # If you don't jump directly to 404
    
        if form.validate_on_submit():  # POST
            data = form.data
            m = Preview.query.filter_by(title=data['title']).count()
            if m == 1 and form.logo.data.filename == "":
                flash("Preview already exists", "err")
                return redirect(url_for("admin.preview_edit", id=id))
            preview.title = data["title"]
    
            # Prepare path
            if not os.path.exists(app.config["UP_DIR"]):
                os.mkdir(app.config["UP_DIR"])
                os.chmod(app.config["UP_DIR"], "rw")
    
            if form.logo.data.filename != "":
                file_logo = secure_filename(form.logo.data.filename)
                preview.logo = changeFileName(file_logo)
                form.logo.data.save(app.config["UP_DIR"] + preview.logo)
    
            db.session.add(preview)  # modify
            db.session.commit()
            flash("Modified successfully", "ok")
            return redirect(url_for('admin.preview_list', page=1))
        return render_template("admin/previewedit.html", form=form, preview=preview)  # Render the original information
    

Member management

  • I don't know why there is a problem connecting to the database when starting the web page. pip install cryptography is required
    pip install -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com cryptography
    
  • Insert some prepared data into the user table
    -- uuid System generation required
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('rat','1231','1231@123.com','13888888881','rat','1f401.png','d32a72bdac524478b7e4f6dfc8394fc0',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('cattle','1232','1232@123.com','13888888882','cattle','1f402.png','d32a72bdac524478b7e4f6dfc8394fc1',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('tiger','1233','1233@123.com','13888888883','tiger','1f405.png','d32a72bdac524478b7e4f6dfc8394fc2',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('rabbit','1234','1234@123.com','13888888884','rabbit','1f407.png','d32a72bdac524478b7e4f6dfc8394fc3',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('Loong','1235','1235@123.com','13888888885','Loong','1f409.png','d32a72bdac524478b7e4f6dfc8394fc4',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('snake','1236','1236@123.com','13888888886','snake','1f40d.png','d32a72bdac524478b7e4f6dfc8394fc5',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('horse','1237','1237@123.com','13888888887','horse','1f434.png','d32a72bdac524478b7e4f6dfc8394fc6',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('sheep','1238','1238@123.com','13888888888','sheep','1f411.png','d32a72bdac524478b7e4f6dfc8394fc7',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('monkey','1239','1239@123.com','13888888889','monkey','1f412.png','d32a72bdac524478b7e4f6dfc8394fc8',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('chicken','1240','1240@123.com','13888888891','chicken','1f413.png','d32a72bdac524478b7e4f6dfc8394fc9',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('dog','1241','1241@123.com','13888888892','dog','1f415.png','d32a72bdac524478b7e4f6dfc8394fd0',now());
    insert into user(name,pwd,email,phone,info,face,uuid,addtime) values('pig','1242','1242@123.com','13888888893','pig','1f416.png','d32a72bdac524478b7e4f6dfc8394fd1',now());
    
  • The picture is also ready and copied to uplaods
  • The user added here is triggered by the front end and belongs to the logic of home; The list/edit/del operation needs to be written
    # Paging: preparing data in the background and macro rendering in the foreground
    # Query parameters are optional. page must be passed every time after / < int: page > is defined
    #Member list
    @admin.route('/user/list/<int:page>', methods=['GET'])
    @admin_login
    def user_list(page=None):
        if page is None:
            page = 1
        page_data = User.query.order_by(
            User.addtime.desc()
        ).paginate(page=page, per_page=4)
        return render_template('admin/userlist.html', page_data=page_data)
    
    # View member details
    @admin.route('/user/view/<int:id>')
    @admin_login
    def user_view(id):
        user = User.query.get_or_404(int(id))
        return render_template('admin/userview.html', user=user)
    
    @admin.route('/user/del/<int:id>', methods=['GET'])
    @admin_login
    def user_del(id):
        user = User.query.get_or_404(int(id)) # If you don't jump directly to 404
        db.session.delete(user)    # Comments and other data are associated with movie. Movie is the main table, so it will be deleted together
        db.session.commit()
        flash("Member deleted successfully!", "ok")  # Lowercase ok
        return redirect(url_for("admin.user_list", page=1))    # Redirection, template rendering includes add edit and list
    
  • The status (normal / frozen) is not set first

Comment management

  • Similarly, initialize some data
-- Reset the self increasing starting point after clearing the table
ALTER TABLE comment auto_increment=1;
-- comment Table foreign key constraint movie Watch, user surface
insert into comment(movie_id,user_id,content,addtime) values(7,1,"good-looking",now());
insert into comment(movie_id,user_id,content,addtime) values(7,2,"not bad",now());
insert into comment(movie_id,user_id,content,addtime) values(7,3,"classic",now());
insert into comment(movie_id,user_id,content,addtime) values(7,4,"Awesome",now());
insert into comment(movie_id,user_id,content,addtime) values(8,5,"ugly",now());
insert into comment(movie_id,user_id,content,addtime) values(8,6,"boring",now());
insert into comment(movie_id,user_id,content,addtime) values(8,7,"dull",now());
insert into comment(movie_id,user_id,content,addtime) values(8,8,"Insensitivity",now());
  • Note to associate queries, including comment lists and deletions
    # Comment list
    @admin.route('/comment/list/<int:page>')
    @admin_login
    def comment_list(page=None):
        if page==None:
            page=1
        page_data = Comment.query.join(
            Movie
        ).join(
            User
        ).filter(
            Movie.id == Comment.movie_id,
            User.id == Comment.user_id
        ).order_by(
            Comment.addtime.desc()
        ).paginate(page=page, per_page=5)
        # {{v.movie.title}}
        return render_template('admin/commentlist.html', page_data=page_data)
    
    @admin.route('/comment/del/<int:id>', methods=['GET'])
    @admin_login
    def comment_del(id):
        comment = Comment.query.get_or_404(int(id)) # If you don't jump directly to 404
        db.session.delete(comment)    # Comments and other data are associated with movie. Movie is the main table, so it will be deleted together
        db.session.commit()
        flash("Comments deleted successfully!", "ok")  # Lowercase ok
        return redirect(url_for("admin.comment_list", page=1))
    

Film collection

  • Previously, an additional field was added to the database: alter table movcollec drop content;
  • Movie collection list, delete collection
    # Favorite list
    @admin.route('/collect/list/<int:page>')
    @admin_login
    def collect_list(page=None):
        if page==None:
            page=1
        page_data = MovCollection.query.join(
            Movie
        ).join(
            User
        ).filter(
            Movie.id == MovCollection.movie_id,
            User.id == MovCollection.user_id
        ).order_by(
            MovCollection.addtime.desc()
        ).paginate(page=page, per_page=8)
        return render_template('admin/collectlist.html', page_data=page_data)
    
    @admin.route('/collect/del/<int:id>', methods=['GET'])
    @admin_login
    def collect_del(id):
        collect = MovCollection.query.get_or_404(int(id)) # If you don't jump directly to 404
        db.session.delete(collect)
        db.session.commit()
        flash("Collection deleted successfully!", "ok")
        return redirect(url_for("admin.collect_list", page=1))
    

Administrator password modification

  • After submitting a new password, we finally need to define a new form model. Here we also need to write a method to verify the old password
    @admin.route('/cpwd', methods=['GET', 'POST'])
    @admin_login
    def cpwd():
        form = PwdForm()
        if form.validate_on_submit():
            data = form.data
            # Here, you need to pass the named parameter and the corresponding field name; Otherwise, the parameter will be abnormal
            admin = Admin.query.filter_by(name=session['admin']).first()
            from werkzeug.security import generate_password_hash
            admin.pwd = generate_password_hash(data['new_pwd'])
            db.session.add(admin)
            db.session.commit()
            flash("Password changed successfully! Login again",'ok')
            return redirect(url_for('admin.logout'))
        return render_template('admin/pwd.html', form=form)
    
  • The password should be encrypted and stored. The password in user needs to be encrypted in the background of home
  • Displays the online time of the administrator
@admin.context_processor
def tpl_extra():
    '''
    Context processor
    :return:
    '''
    # Create global variables and render them directly in admin.html
    data = dict(
        online_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    )

Log management

  • The processing logic is different according to the specific log requirements

Operation log

  • This part needs to generate data during various operations, such as adding tag logs, adding movie logs, and so on
    # For example, when adding labels:
    def tag_add():
        form = TagForm()
        if form.validate_on_submit():   # No verification
            data = form.data
            tag = Tag.query.filter_by(name=data['name']).count()
            if tag == 1:
                flash("Label already exists!", "err")
                return redirect(url_for('admin.tag_add'))
            tag = Tag(
                name= data['name']
            )
            db.session.add(tag)
            db.session.commit()
            flash("Tag added successfully", 'ok')  # The second parameter is a fixed number of values that are applied to the template category_filter=["ok"]
            # Add operation log---------------------
            oplog = OperateLog(
                admin_id=session['admin_id'],
                ip=request.remote_addr,
                reason="Add label %s" % data['name']
            )
            db.session.add(oplog)
            db.session.commit()
            
        return render_template('admin/tagadd.html', form=form)
    
  • The follow-up part needs to be improved by itself, and the operation log shall be recorded after various operations
  • With the operation log, of course, it should be displayed. The logged in administrator can now see all the operation logs
    # Operation log
    @admin.route('/oplog/list/<int:page>')
    @admin_login
    def oplog_list(page=None):
        if page==None:
            page=1
        page_data = OperateLog.query.join(
            Admin
        ).filter(
            Admin.id == OperateLog.admin_id
        ).order_by(
            OperateLog.addtime.desc()
        ).paginate(page=page,per_page=3)
        return render_template('admin/oploglist.html', page_data=page_data)
    

Administrator login log

  • This part needs to generate data when logging in
    def login():
    	xxx
    	# Add login log-----------------
        admin = AdminLog(
            admin_id = admin.id,
            ip=request.remote_addr
        )
        db.session.add(admin)
        db.session.commit()
        xxx
    
  • list view logic
    # Administrator login log
    @admin.route('/adminlog/list/<int:page>')
    @admin_login
    def adminlog_list(page=None):
        if page==None:
            page=1
        page_data = AdminLog.query.join(
            Admin
        ).filter(
            Admin.id == AdminLog.admin_id
        ).order_by(
            AdminLog.addtime.desc()
        ).paginate(page=page,per_page=2)
        return render_template('admin/adminloglist.html', page_data=page_data)
    
  • Front end paging rendering

Member login log

  • Because this part belongs to the front and background management of home, only a few pieces of data can be initialized directly
    insert into userlog(user_id,ip,addtime) values(1,"192.168.4.1",now());
    insert into userlog(user_id,ip,addtime) values(2,"192.168.4.2",now());
    insert into userlog(user_id,ip,addtime) values(3,"192.168.4.3",now());
    insert into userlog(user_id,ip,addtime) values(4,"192.168.4.4",now());
    insert into userlog(user_id,ip,addtime) values(5,"192.168.4.5",now());
    insert into userlog(user_id,ip,addtime) values(6,"192.168.4.6",now());
    insert into userlog(user_id,ip,addtime) values(7,"192.168.4.7",now());
    insert into userlog(user_id,ip,addtime) values(8,"192.168.4.8",now());
    insert into userlog(user_id,ip,addtime) values(9,"192.168.4.9",now());
    
  • View logic
    # Member login log
    @admin.route('/userlog/list/<int:page>')
    @admin_login
    def userlog_list(page=None):
        if page==None:
            page=1
        page_data = UserLog.query.join(
            User
        ).filter(
            User.id == UserLog.user_id
        ).order_by(
            UserLog.addtime.desc()
        ).paginate(page=page,per_page=4)
        return render_template('admin/userloglist.html', page_data=page_data)
    
  • Summary
    • The logical definitions of administrators, members, movies, labels, logs, etc. in the front and back of admin are completed
      *The next section focuses on role-based access control

Tags: Python Flask

Posted on Wed, 01 Dec 2021 21:06:13 -0500 by jswash