赞
踩
以下内容您将了解如何使用Django快速搭建网站
在腾讯云购买域名备案到部署云服务器正式上线
顺便学习总结相关网页前端和数据库的基础操作
本章为零基础学习Django快速获得学习反馈,
供本人学习复盘使用也不具任何学习观摩性。
系统版本:MacOS Monterey 12.4
芯片版本:Apple M1 Max
软件版本:Python 3.8
数据编辑:DB Browser for SQLite Version 3.12.2
编辑版本:PyCharm 2022.1.1
其他服务:CentOS 8.2 / 宝塔 / 阿里云 / 腾讯云
…
打开terminal,输入如下代码:
# temrinal输入
python3.8 -m pip install Django
…
# temrinal输入
python3.8 -m django --version
…
# temrinal输入
django-admin startproject website
…
# terminal输入
python3 manage.py startapp webapp
…
# terminal输入
python3.8 manage.py runserver
…
# 浏览器输入
http://127.0.0.1:8000/
…
# setting.py输入
ALLOWED_HOSTS = ['*',]
注:找到并更改ALLOWED_HOSTS就可开启局域网
setting.py可以在项目文件中找到
…
新建app/urls
/(project)/(app)/urls
创建URLconf
# project/app/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
插入URLconf
# project/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('(appitem)/', include('(appitem).urls')),
path('admin/', admin.site.urls),
]
python文件更改
# manage.py输入
execute_from_command_line(['manage.py', 'runserver', '0.0.0.0:2516'])
启用端口
# project/terminal输入
python3.8 manage.py runserver 0.0.0.0:8000
清空端口(若需)
# Question: ( 报错 )
Django-Error: That port is already in use
# Answer: ( 查询正在使用的进程并终止 )
lsof -i:oooo
kill -9 nnnn
# 或GoogleCloud
sudo fuser -k 80/tcp
…
# 浏览器输入
http://192.168.x.xx:8000/
注:ip地址替换成局域网设备地址
按住Option点击MacTopBar中的无线图标,查阅并获得ip地址
代码中192.168.x.xx处即为ip地址替换处
可以使用外部手机或电脑进行测试访问
若无法连接,重新输入以下指令并确认端口号一致
python3.8 manage.py runserver 0.0.0.0:8000
…
编辑模型
from django.db import models from django.utils.translation import gettext_lazy as _ from django.utils.encoding import smart_str class Music(models.Model): title = models.CharField(_(u'名称'),max_length=250) author = models.CharField(_(u'作者'),max_length=250) url = models.CharField(_(u'地址'),max_length=250) createdate = models.DateTimeField(_ (u'创建时间'), auto_now_add=True, blank=True ) def __unicode__(self): return smart_str(self.title) class Meta: verbose_name = _(u'音乐库') verbose_name_plural = _(u'音乐库') ordering=['-createdate']
激活模型
INSTALLED_APPS = [
...
'(app-item).apps.(App-item)Config',
]
模型迁移
python3.8 manage.py makemigrations (App-item)
python3.8 manage.py migrate
模型删除(若需)
python3.8 manage.py migrate (App-item) zero
创建管理员
python3.8 manage.py createsuperuser
通知Admin站点
from django.contrib import admin
from .models import *
admin.site.register(Music)
数据管理页面
python3.8 manage.py runserver 0.0.0.0:8000
http://0.0.0.0:8000/admin/login/?next=/admin/
离线数据库编辑
# 离线浏览及编辑数据库
DB Browser for SQLite Version 3.12.2
# python 数据处理工具 # --------------------------------------------------------- # 数据插入 def multi_sql_insert(db_path,entities,point_id,point_init,table): ''' - 数据嵌入 :param db_path: '~/db.splite3' :param entities: [(0,'','',...),(0,'','',...),...] :param point_id: 'id,title,author,...' :param point_init: '?,?,?,...' :param table: 'Database' ''' import sqlite3 db = sqlite3.connect(db_path) cursorObj = db.cursor() cursorObj.execute(f'SELECT * FROM {table}') rowcount = cursorObj.fetchall() for e in range(len(entities)): entity = entities[e] add_row = str(len(rowcount)+e) cursorObj.execute( f"INSERT INTO {table}({point_id}) " f"VALUES({point_init})", tuple([add_row] + list(entity[1:])) ) db.commit() db.close() # 数据修改 def multi_sql_update(db_path,updates,table,condition): ''' - 数据修改更新 :param db_path: '~/db.splite3' :param updates: [['title','John']] -> (point:value) :param table: 'Database' :param condition: 'id = 1' / 'WHERE price > 100'/ ... ''' import sqlite3 db = sqlite3.connect(db_path) for updt_i in range(len(updates)): update_point = updates[updt_i][0] updt_value = updates[updt_i][1] def sql_update(db,condition,update_point,updt_value,table): cursorObj = db.cursor() cursorObj.execute(f'UPDATE {table} SET ' f'{update_point} = "{updt_value}" ' f'where {condition}' ) db.commit() sql_update(db,condition,update_point,updt_value,table) db.close() # 数据查询 def sql_fetch(db_path,point): ''' - 数据查询 :param db_path: '~/db.splite3' :param point: 'id,name' :return: rows = ('','','',...) ''' import sqlite3 db = sqlite3.connect(db_path) cursorObj = db.cursor() cursorObj.execute(f'SELECT {point} FROM {table}') rows = cursorObj.fetchall() db.close() # print(rows) return rows # 数据删除 def sql_delete(db_path,table,condition): ''' - 数据行删除 :param db_path: '~/db.splite3' :param table: 'Database' ''' import sqlite3 db = sqlite3.connect(db_path) cursorObj = db.cursor() cursorObj.execute(f'DELETE FROM {table} WHERE {condition}') db.commit() db.close()
Q & A
a. 账号记得 | 密码忘了
终端输入
python3 manage.py shell
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='你的管理员账号')
>>> user.set_password('你想设置的新密码')
>>> user.save()
>>> quit()
b. 账号忘了 | 密码忘了:
终端输入
python3 manage.py shell
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(pk=1)
>>> user
<User: 管理员账号>
>>> user = User.objects.get(username='你的管理员账号')
>>> user.set_password('你想设置的新密码')
>>> user.save()
>>> quit()
…
前端获得用户验证输入(Get)| 后端对比用户验证信息(Auth)
a. 输入模型
# models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
USERNAME_FIELD = 'username'
username = models.CharField('username', max_length=128, blank=True)
password = models.CharField('password', max_length=50, blank=True)
mod_date = models.DateTimeField('Last modified', auto_now=True)
class Meta: verbose_name = 'User Profile'
def __str__(self):
return "{}'s profile".format(self.user.__str__())
注:一定要设置USERNAME_FIELD
b. 配置注册
# setting.py
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
c. urlpatterns
# urls.py
path('login_check/', views.login_check, name='login_check')
d. 添加视图
from django.contrib.auth import authenticate,login
def login_check(requst):
if requst.method == 'GET':
username = requst.GET.get('username')
password = requst.GET.get('password')
user = authenticate(username=username,password=password)
if user is not None:
login(requst, user)
return redirect('index')
else:
return render(requst,'signin.html')
else:
return render(requst,'signin.html')
注:确认身份后登录要使用跳转redirect(name)
若使用render会在url地址中体现用户名和密码
e. 页面引入
# html
<form action="{% url 'login_check' %}" ... method="GET">
<input name="username" ...>
<input name="password" ...>
…
from django.contrib.auth.models import User def create_user(requst): if requst.method == "GET": username = requst.GET.get('username') password = requst.GET.get('password') email = requst.GET.get('email') confirm = requst.GET.get("confirm_password") if password == "" or username == "": alert_box(requst, "用户名或密码不能为空") elif password != confirm: alert_box(requst, "两次密码不一致") elif User.objects.filter(username=username): alert_box(requst, "该用户名已存在") else: new_user = User.objects.create_user(username=username, password=password,email=email) new_user.save() return redirect('index') return render(requst, 'signup.html')
…
def signout(requst):
logout(requst)
return render(requst,'signin.html')
…
a. view.py
from django.contrib import messages
def alert_box(requst,message):
messages.success(requst,message)
b. html 加载在body内
<!-- message box -->
{% if messages %}
<script>
{% for msg in messages %}
alert('{{ msg.message }}');
{% endfor %}
</script>
{% endif %}
<!-- end message box -->
…
# 发送邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# smpt服务器地址 [建议使用163]
EMAIL_HOST = 'smtp.163.com'
# 邮箱端口
EMAIL_PORT = 25
# 发送邮件的邮箱
EMAIL_HOST_USER = 'xxx@163.com'
# ***客户端授权密码!!**
# 需要去163邮箱开启smpt服务并获得唯一授权码
EMAIL_HOST_PASSWORD = '!@#$%^&*$%^@#'
# 收件人看到的发件人
EMAIL_FROM = 'xxx<xxx@163.com>'
# 避免报错加上
DEFAULT_FROM_EMAIL = 'xxx@163.com'
from django.shortcuts import render, HttpResponse from django.core.mail import send_mail, EmailMultiAlternatives from django.conf import settings from email.header import make_header from email.mime.text import MIMEText from email.mime.image import MIMEImage import os def send_simple_email(request): subject = "[邮件主题]" message = "[邮件内容]" from_email = settings.EMAIL_FROM recipient_list = ["xx@xx.com","xxx@xx.com"...] ret = send_mail(subject, message, from_email, recipient_list) return HttpResponse(ret) def send_complex_email(request): subject = '' text_content = '' html_content = '' from_email = settings.DEFAULT_FROM_EMAIL receive_email_addr = ["xx@xx.com"] msg = EmailMultiAlternatives(subject, text_content, from_email, receive_email_addr) msg.attach_alternative(html_content, "text/html") # 发送图像 html1 = "<div><img src='cid:imgid'></div>" msg_html_img = MIMEText(html1, 'html', 'utf-8') msg.attach(msg_html_img) file_path = os.path.join(settings.BASE_DIR, "static/kd.png") with open(file_path, "rb") as f: msg_img = MIMEImage(f.read()) msg_img.add_header('Content-ID', 'imgid') msg.attach(msg_img) # 发送txt附件 file_path = os.path.join(settings.BASE_DIR, "日志.txt") text = open(file_path, 'rb').read() file_name = os.path.basename(file_path) b = make_header([(file_name, 'utf-8')]).encode('utf-8') msg.attach(b, text) # 发送jpg附件 file_path = os.path.join(settings.BASE_DIR, "test.jpg") text = open(file_path, 'rb').read() file_name = os.path.basename(file_path) b = make_header([(file_name, 'utf-8')]).encode('utf-8') msg.attach(b, text) # 发送xlsx附件 file_path = os.path.join(settings.BASE_DIR, "test.xlsx") text = open(file_path, 'rb').read() file_name = os.path.basename(file_path) b = make_header([(file_name, 'utf-8')]).encode('utf-8') msg.attach(b, text) # msg.attach_file(file_path) msg.send() return HttpResponse("发送完成")
from django.urls import path
from . import views
urlpatterns = [
path("send_simple_email/", views.send_simple_email, name="send_simple_email"),
path("send_complex_email/", views.send_complex_email, name="send_complex_email"),
]
…
如果部署在外服务器,如:AWS,需要对TLS和SSL进行特别设置
163邮箱配置
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = 'xxx'
EMAIL_HOST_PASSWORD = 'xxx' # 这里是授权码
# 下面两项只能有一个为True
EMAIL_USE_TLS = False
EMAIL_USE_SSL = True
gmail邮箱的配置
# 出现认证问题参考 -> https://segmentfault.com/q/1010000008458788
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'xxx'
EMAIL_HOST_PASSWORD = 'xxx'
# 下面两项只能有一个为True
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
创建装饰器
# project/app/decorator.py from django.shortcuts import render from django.http import HttpResponse def already_login(func): def alr_login(request, *args, **kwargs): outsec = 60*60*3 request.session.set_expiry(outsec) # 超过秒数后失效 # import datetime # outday = datetime.datetime.now() + datetime.timedelta(days=30) # request.session.set_expiry(outday) # 超过日期后时效 # request.session.set_expiry(0) # 关闭浏览器后失效 # request.session.set_expiry(None) # 遵循全局失效策略 if request.user.is_authenticated: return func(request, *args, **kwargs) else: return render(request,'signin.html') return alr_login # def validate_permission(func): # def valid_per(request, *args, **kwargs): # group_id = request.session.get('group_id') # if group_id == 0: # return func(request, *args, **kwargs) # else: # return HttpResponse("无权访问") # return valid_per
配置需要权限的视图
# views.py
from MusicStore.decorator import already_login
@already_login
def index (request):
return render(request,'index.html')
向email发送验证码
def email_code(requst): username = requst.GET.get('username') has_username = User.objects.filter(username=username) if has_username: def random_str(randomlength=4): import random codekey = '' chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' length = len(chars) - 1 for i in range(randomlength): codekey += chars[random.randint(0, length)] return codekey subject = '验证码' text_content = '重置密码' code = random_str() requst.session["code"]=code requst.session["username"] = username email = User.objects.get(username=username).email requst.session["email"] = email html_content = f'<h3>重置验证码,' \ f'请谨慎保管</h3><h1>' \ f'<font style="background-color:darkgray;color: #3F3F3F" >{code}</font></h1>' from_email = settings.DEFAULT_FROM_EMAIL receive_email_addr = [email] msg = EmailMultiAlternatives(subject, text_content, from_email, receive_email_addr) msg.attach_alternative(html_content, 'text/html') msg.send() return redirect('password') else: alert_box(requst,'Email尚未注册') return redirect('forgot')
修改密码
@validate_codemail def pswrd_reset(requst): code = requst.GET.get("code") password = requst.GET.get("password") email = requst.session['email'] username = requst.session["username"] user = User.objects.get(username=username) confirm_password = requst.GET.get("confirm_password") if confirm_password == password: has_username = User.objects.filter(username=username,email=email) if has_username: if code == requst.session['code']: user.set_password(password) user.save() requst.session.flush() alert_box(requst,'密码重置成功') return redirect('signin') else: requst.session.flush() alert_box(requst,'验证码不正确') return redirect('forgot') else: requst.session.flush() alert_box(requst, '用户名尚未注册') return redirect('forgot') else: alert_box(requst, '两次密码不一致') return redirect('password')
装饰器
def validate_codemail(func):
def valid_codmail(request, *args, **kwargs):
session = len(request.session.items()) # 判断是否有验证码请求记录
if session > 0:
return func(request, *args, **kwargs)
else:
return redirect('forgot')
return valid_codmail
urls
urlpatterns = [
...
path('email_code/', views.email_code, name='email_code'),
path('pswrd_reset/',views.pswrd_reset,name='pswrd_reset'),
]
前端
...
<form action="{% url 'email_code' %}"... method="GET">
<input name="username"... placeholder="用户名">
...
<input name="code"... placeholder="验证码">
<input name="password" ... placeholder="新 密 码">
<input name="confirm_password" ... placeholder="确认密码">
…
from django.contrib.auth.hashers import make_password, check_password
sha256_password = make_password("123456", None, 'pbkdf2_sha256') # 加密
checkbool = check_password("123456",'pbkdf2_sha256')# 校验
pip3.8 insatall django-ratelimit
@ratelimit(key='ip', rate='5/h',block=True)
your_views(requst):
...
https://django-ratelimit.readthedocs.io/en/stable/usage.html
…
安装GeoLite2并下载IP数据库
# 1. 安装geoip2
pip3.8 install geoip2
# 2. 下载City和Country数据库
搜索-> GeoLite2免费下载
...
创建 middleware.py ,与 settings.py 同目录下
from django.http import HttpResponse from django.utils.deprecation import MiddlewareMixin # 1.10.x class TestMiddleware(MiddlewareMixin): def process_view(self,request,view_func,*view_args,**view_kwargs): def get_ip_location(): ''' -获得ip位置信息 :param request: :param datapath: :return: country(string) ''' import geoip2.database datapath = os.path.join(IPDIA_ROOT,'GeoLite2-City.mmdb') reader = geoip2.database.Reader(datapath) try: response = reader.city(ip) country = response.country.iso_code cityname = response.city.name data = {'country': country, 'city': cityname} return data['country'] except: local_ips = ['127.0.0.1'] if ip in local_ips: return 'LOCAL' else: return 'Unkown IP' if 'HTTP_X_FORWARDED_FOR' in request.META: ip = request.META['HTTP_X_FORWARDED_FOR'] else: ip = request.META['REMOTE_ADDR'] # 使用GeoLite2数据库判别 id_country = get_ip_location() print(f'[{id_country}] -> {ip} ') countries = ['CN','TW','HK','LOCAL'] if id_country not in countries: return HttpResponse('<h1 style="opacity:0.2">no permission</h1>')
setting.py
MIDDLEWARE = [
...
'(django项目名).middleware.TestMiddleware',
]
urls.py
...
from MusicStore.views import *
handler403 = page_403
handler404 = page_404
...
views.py
# setting.py
DEBUG = False
...
def page_404 (request, exception, template_name='404.html'):
return render(request,template_name)
DEBUG = False
<!-- MacOS系统下Debug默认为True,关闭成False后静态素材和样式丢失的解决办法 -->
python3.8 manage.py runserver 0.0.0.0:8000 --insecure
安装组件
pip3.8 install djangorestframework
pip3.8 install markdown
pip3.8 install django-filter
添加项目
INSTALLED_APPS = [
...
'rest_framework',
]
配置模型
# setting.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
创建API
# (project)/urls.py from django.contrib import admin from django.urls import include, path from (appitem).models import * from rest_framework import routers, serializers, viewsets # Serializers define the API representation. class MusicSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Music fields = ['title', 'author', 'url', 'createdate'] # ViewSets define the view behavior. class MusicViewSet(viewsets.ModelViewSet): queryset = Music.objects.all() serializer_class = MusicSerializer # Routers provide an easy way of automatically determining the URL conf. router = routers.DefaultRouter() router.register(r'music', MusicViewSet) urlpatterns = [ path('', include(router.urls)), path('(appitem)/', include('(appitem).urls')), path('admin/', admin.site.urls), path(r'^api-auth/', include('rest_framework.urls')) ]
运行项目
python3.8 manage.py migrate
python3.8 manage.py runserver
http://0.0.0.0:8000/music/
```
# 通过转化Query第一列数据为dict字典类型,获取keys
# Query = [{},{},...]
obj = Obj()
Query_Keys = dict(obj.objects.values()[0]).keys()
# 获取Query所有键 query_keys = list(dict(Music.objects.values()[0]).keys()) # 需要手工添加的字典键内容 handle_keys = ['aaa', 'bbb'] new_dict = {} new_dict.update({ 'aaa': '', 'bbb': '', }) # 默认值添加 for key in query_keys: if key not in handle_keys: new_dict.update({key: '-'}) # 非指定值都用-填写 # 创建数据 Music.objects.create(**new_dict)
…
# 浏览器输入: 挑选下载一个喜欢的html模板
注: 找一些比较成熟综合素材网
UI/UX设计可以降维用于日常测试
适合平时制作ppt和影视资源使用
…
映射跳转链接
# 替换模板中的页面跳转 | 新建substitution.py def hyperlink_url_modify(): from tqdm import tqdm page_path = '~' pages = [i for i in os.listdir(page_path) if i.endswith('.html')] # 遍历所有同类html模板 for page_i in tqdm(range(len(pages))): index_file = os.path.join(page_path,pages[page_i]) f = open(index_file,'r') # 链接映射 lines = [] for line in f.readlines(): new_line = line for page_ii in range(len(pages)): html_nm = os.path.basename(pages[page_ii]) page_nm = html_nm.split('.html')[0] source_nm = '"'+ html_nm + '"' subs_nm = '"{% url ' + '\'' + page_nm + '\'' + ' %}"' if '404' in page_nm: subs_nm = '"{% url ' + '\'' + 'page_404' + '\'' + ' %}"' new_line = new_line.replace(source_nm, subs_nm) lines.append(new_line) new_lines = ''.join(lines) # 覆盖原文件 f = open(index_file,'w') f.write(new_lines) f.close() print('[ Static Folder Modify Finished !]')
映射static物料
# 替换模板中的物料静态地址 | 新建substitution.py def static_url_modify(): page_path = '~' pages = [i for i in os.listdir(page_path) if i.endswith('.html')] for page_i in tqdm(range(len(pages))): index_file = os.path.join(page_path,pages[page_i]) f = open(index_file,'r') lines = [] for line in f.readlines(): new_line = line \ # .replace('"image', '"../static/image') # .replace('"icon', '"../static/icon') # .replace('"css', '"../static/css')\ # .replace('"js', '"../static/js')\ # .replace('"img', '"../static/img') # .replace('assets','../static') # .replace('(assets', '(../static/assets') \ # .replace('"assets/', '"../static/assets/') \ # .replace('./','../static/')\ # .replace('"images/','"../static/images/')\ # .replace('"css/', '"../static/css/') \ # .replace('"libs/', '"../static/libs/') \ # .replace('"scripts/', '"../static/scripts/')\ # .replace('\'images', '\'../static/images') lines.append(new_line) new_lines = ''.join(lines) f = open(index_file,'w') f.write(new_lines) f.close()
批量views
# views.py
def multi_def_generate():
page_path = '~'
pages = [i for i in os.listdir(page_path) if i.endswith('.html')]
lines = ''
for page_i in tqdm(range(len(pages))):
page_nmfull = pages[page_i]
page_nm = pages[page_i].replace('.html','')
if page_nm.startswith('404'):
def_pattern = f'def page_40(request):\n return render(request,\'{str(page_nmfull)}\')\n\n'
else:
def_pattern = f'def {page_nm}(request):\n return render(request,\'{str(page_nmfull)}\')\n\n'
lines += def_pattern
print(lines)
批量urlspattern
# (project)/urls.py
def urlspatterns_generate():
page_path = '~'
pages = [i for i in os.listdir(page_path) if i.endswith('.html')]
lines = ''
for page_i in tqdm(range(len(pages))):
page_nm = pages[page_i].replace('.html','')
if page_nm.startswith('404'):
urlspattern = f'path(\'{page_nm}/\', views.page_404, name=page_404),\n'
else:
urlspattern = f'path(\'{page_nm}/\', views.{page_nm}, name=\'{page_nm}\'),\n'
lines += urlspattern
print(lines)
…
创建templates
# 该文件夹将用于存放html内容
(project)/(item)/templates
导入html
# 将模板索引页拖入templates中
(project)/(item)/templates/index.html
(project)/(item)/templates/home.html
...
views新建
# views.py输入 ( path一定要设置 name= ' ' 方便后面调用网页 )
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index,name='index'),
path('home/', views.home,name='home'),
...
]
urls新建
# url.py输入
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
def home(request):
return render(request, 'home.html')
...
DIRS设置
# setting.py 添加DIRS
import os
TEMPLATES = [
{ ...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
... }
]
INSTALLED_APPS设置
# setting.py 添加所建的App
import os
INSTALLED_APPS = [
[ ...
'xxx-app',
...
]
…
# 该文件夹将用于存放物及料配置内容 (位置与templates同级)
(project)/(item)/static
# 文件移动 (该文件夹将用于存放Images、JavaScript、CSS等文件)
(project)/(item)/static/images
(project)/(item)/static/css
(project)/(item)/static/js
...
setting.py 设置
# 为了部署时将静态文件复制到所有服务端都可以访问的文件夹
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'collectstatic/')
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
manage.py 收集
# MacOS Project/Terminal执行收集程序
python manage.py collectstatic
注释
# 关于STATIC的注释
STATIC_URL = static地址 [让网页可以访问到静态文件]
STATIC_ROOT = 收集归档所有static文件
[让静态文件夹可被所有客户端访问到]
[可以避免多个app需要多个静态目录]
[名字自取但需和uwsgi.ini中路径一致]
STATICFILES_DIRS = [让Django同时在App的内、外搜索静态文件]
…
# html链接映射(pagename在urlpatterns里设置)
# 替换前 <a href="about.html">关于</a>
# 替换后 <a href="{% url 'home' %}">关于</a>
{% url 'home' %}
# 物料链接映射
../static/images/xxx.png
../static/css/xxx.css
../static/js/xxx.js
…
# project/terminal输入
python3.8 manage.py
# 退出项目
control + c
…
动画效果及排版等正常显示
…
<!-- loaing effects --> {% if # %} <div class="loading"> <span></span> <span></span> <span></span> <span></span> <span></span> </div> <style type="text/css"> * { padding: 0; margin: 0; } .loading{ height: 25px; margin: 100px auto; display: flex; justify-content: center; } .loading span{ width: 6px; height: 100%; border-radius: 4px; background-color: lightgreen; animation: load 1s ease infinite; margin: 0 2px; } @keyframes load{ 0%, 100% { transform: scaleY(1.2); background-color: lightgreen; } 50% { transform: scaleY(0.3); background-color: lightblue; } } .loading span:nth-child(2){ animation-delay: 0.2s; } .loading span:nth-child(3){ animation-delay: 0.4s; } .loading span:nth-child(4){ animation-delay: 0.6s; } .loading span:nth-child(5){ animation-delay: 0.8s; } </style> {% endif %} <!-- end loaing effects -->
…
view.py
# 需以字典方式传参
def yourView(request):
var = xxx
return render(request,'phones.html',context={'msg':var})
or
def yourView(request):
var = {
'':''
}
return render(request,'phones.html',context=var)
.html
<span> {{ msg }} </span>
ForEach
{% for i in msg %}
<li> {{i.title}} </li>
{% endfor %}
…
隐藏滚动条
::-webkit-scrollbar{width:0;}
启动滚动条
.overflow-x-auto {
overflow-x: auto
}
// html | 增加变量id,为id添加类并启动
{% for i in is %}
<div id="id{{ forloop.counter }}" class="" ></div>
{% end for %}
<script src="../static/js/jquery-3.5.1.min.js"></script>
<script>
$("#id{{ forloop.counter }}").addClass('xxx').toggleClass('nnn')
</script>
/* css */
.xxx {
height: 115px;
width: 100%;
position: absolute;
bottom: 0;
background: rgba(255, 255, 255, 0.85);
transform: translateY(35px);
transition: all 0.7s ease-in-out;
}
.nnn {
transform: translateY(0px);
}
<!-- 需要为script添加module类型 -->
<script src="../static/js/xxx.js" type="module"></script>
import xxx from '../.../../'
var xxx = function(){
...
}
<!-- html 需要为script添加id并新增data属性 -->
<script id="testScript" src="../static/js/albumplay.js" data="testscript"></script>
// .js
var data = document.getElementById('testScript').getAttribute('data').toString()
jQuery - 用于简化选取HTML元素,并对它们执行"操作"
https://blog.csdn.net/u012932876/article/details/117465004?spm=1001.2014.3001.5506
<button id="btn">ClickMe</button>
<script src="../static/js/jquery-3.5.1.min.js"></script>
<script>
$(function (){
$("#btn").click(function (){
$.ajax({
type : "post",
url : "{% url 'handle' %}"
data : {"username":"jacky"},
dataType : "json",
});
});
})
</script>
def handle():
if request.method == 'POST':
collection = Favorite()
username = request.POST.get('username')
<div id=""></div>
<scrip>
$("#id").text(data["msg"])
</scrip>
$ajax({
...
});
return false;
$ajax({
...
success: function(data){
window.location.href="{% url 'xxx' %}";
}
});
// 增加js文件 <getparam.js>
(function ($) {
$.extend({
//1、取值 $.Request("name")
Request: function (name) {
var sValue = location.search.match(new RegExp("[\?\&]" + name + "=([^\&]*)(\&?)", "i"));
//decodeURIComponent解码
return sValue ? decodeURIComponent(sValue[1]) : decodeURIComponent(sValue);
},
});
})(jQuery);
// 前一页传参
<script>
$(function(){
...
name = "";
age = "";
url = "xxx.html?name="+name+"&age="+age;//此处拼接内容
window.location.href = url;
})
</script>
// 后一页获参
<script>
function getData(){
var name = $.Request("name");
var age = $.Request("age");
}
getData()
</script>
<header style="text-indent: 2em; margin-top: 30px;"> <span id="time">4</span><a href="index.html" title="点击访问">跳过</a> </header> <script> function delayURL() { var delay = document.getElementById("time").innerHTML; var t = setTimeout("delayURL()", 1000); if (delay > 0) { delay--; document.getElementById("time").innerHTML = delay; } else { clearTimeout(t); window.location.href = "index.html"; } } delayURL() </script>
var t
$("#ipt").on('input',function (){
clearTimeout(t)
$("#ipt_btn").hide()
t = setTimeout(function (){
$("#ipt_btn").show()
},1500);
)
过程简述
codepen上有很多有趣的svg和动画效果,很多是scss格式而不是css
所以需要研究如何在HTML中引入,大概路径如下:
1. 安装npm
2. 安装sass
3. scss转换css
4. 引入css
安装步骤
1. brew install node
# 报错
(2.0.Error: Command failed with exit 128: git)
# 解决(查看-复制-运行)
2.1 brew -v
# 重装
3. brew install node
# 版本
4. npm -v
# 安装cnpm(淘宝镜像)
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 若安装nrm报错request@2.88.2: request has been deprecated
1. npm config set registry https://registry.npm.taobao.org
2. npm config get registry
3. npm install nrm -g
4. cnpm install node-sass --save-dev
5. cnpm install sass-loader --save-dev
# 在需要转换的sass文件夹下terminal
sass (input.scss) (output.css)
# 使用Django模板标签
data1 = xx.objects.all()
data2 = {
"key": ["value1","value2"],
...
}
msg = {
"data1" : data1,
"data2" : data2,
}
<!-- Html Page -->
{% for k,v in msg %}
<div>{{ k.xx }}</div>
{% for item in k %}
<div>{{ item.yy }}</div>
{% endfor %}
{% endfor %}
view.py
def page(request): from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger song = Music.objects.all() # 获取所有的book paginator = Paginator(song, 30) # 每页10条 page = request.GET.get('page', 1) # 获取页面请求的page页码,默认为第1页 currentPage = int(page) try: song_page = paginator.page(page) # book_list为page对象 except PageNotAnInteger: song_page = paginator.page(1) except EmptyPage: song_page = paginator.page(paginator.num_pages) result = { "book_list" : song_page, "paginator" : paginator, "currentPage" : currentPage, } return render(request, "page.html",result)
.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container"> <h4>分页器</h4> <ul> <!-- 上一页 --> {% for book in book_list %} <div>{{ book.title }} {{ book.length }}</div> {% endfor %} </ul> <ul class="pagination" id="pager"> {% if book_list.has_previous %} <li class="previous"> <a href="/page/?page={{ book_list.previous_page_number }}">上一页</a> </li> {% else %} <li class="previous disabled"><a href="#">上一页</a></li> {% endif %} <!-- 页数 --> {% for num in paginator.page_range %} {% if num == currentPage %} <li class="item active"><a href="/page/?page={{ num }}">{{ num }}</a></li> {% else %} <li class="item"><a href="/page/?page={{ num }}">{{ num }}</a></li> {% endif %} {% endfor %} <!-- 下一页 --> {% if book_list.has_next %} <li class="next"> <a href="/page/?page={{ book_list.next_page_number }}">下一页</a> </li> {% else %} <li class="next disabled"><a href="#">下一页</a></li> {% endif %} </ul> </div> </body> </html>
<!-- 适用于分页及不同歌单播放的综合情况 --> <audio id="audioplayer" controls="controls" hidden><source src=""/></audio> <script src="../static/js/jquery-3.5.1.min.js"></script> <script> if (document.readyState){ var tapid_cache,playlist_cache $("#pausebtn{{ var_1 }}_{{ var_2 }}").hide() document.getElementById("audioplayer").src = "{{ song.url }}" let btn = document.getElementById("playmusic{{ var_1 }}_{{ var_2 }}") btn.onclick = function (){ var audioplayer = document.getElementById("audioplayer") // -> 暂停状态(点击前) if (audioplayer.paused){ // 同一首歌继续播放 if (tapid_cache == "{{ var_2 }}"){ audioplayer.play() } // 不同歌曲切换播放 else { // 通过加载src来重头播放达到停止播放效果 document.getElementById("audioplayer").src = "{{ song.url }}" audioplayer.play() } $("#playbtn{{ var_1 }}_{{ var_2 }}").hide() $("#pausebtn{{ var_1 }}_{{ var_2 }}").show() tapid_cache = "{{ var_2 }}" playlist_cache = "{{ var_1 }}" } // -> 播放状态(点击前) else { // 点击歌曲在歌单内序号相同 if (tapid_cache == "{{ var_2 }}"){ // 同一张歌单 if (playlist_cache == "{{ var_1 }}"){ audioplayer.pause() $("#playbtn{{ var_1 }}_{{ var_2 }}").show() $("#pausebtn{{ var_1 }}_{{ var_2 }}").hide() } // 不同歌单 else { // 停止正在播放内容 audioplayer.pause() $("#pausebtn"+ playlist_cache + "_" + tapid_cache).hide() $("#playbtn" + playlist_cache + "_" + tapid_cache).show() // 更新新内容地址 document.getElementById("audioplayer").src = "{{ song.url }}" // 播放新内容 audioplayer.play() $("#playbtn{{ var_1 }}_{{ var_2 }}").hide() $("#pausebtn{{ var_1 }}_{{ var_2 }}").show() } } // 点击歌曲在歌单内序号不同 else { // 正在播放图标初始化 $("#pausebtn"+playlist_cache + "_" + tapid_cache).hide() $("#playbtn" +playlist_cache + "_" + tapid_cache).show() // 更新新内容地址 document.getElementById("audioplayer").src = "{{ song.url }}" audioplayer.play() $("#playbtn{{ var_1 }}_{{ var_2 }}").hide() $("#pausebtn{{ var_1 }}_{{ var_2 }}").show() } // 记录当前播放标志缓存 tapid_cache = "{{ var_2 }}" playlist_cache = "{{ var_1 }}" } // 音乐播放完初始化播放按钮(实时监视) document.getElementById("audioplayer").ontimeupdate = function (){ if (document.getElementById("audioplayer").ended){ $("#pausebtn"+playlist_cache + "_" + tapid_cache).hide() $("#playbtn" +playlist_cache + "_" + tapid_cache).show() } // 非鼠标点击情况下启动或停止播放键监视按钮样式 if (document.getElementById("audioplayer").paused){ $("#pausebtn"+playlist_cache + "_" + tapid_cache).hide() $("#playbtn" +playlist_cache + "_" + tapid_cache).show() } else { $("#pausebtn"+playlist_cache + "_" + tapid_cache).show() $("#playbtn" +playlist_cache + "_" + tapid_cache).hide() } } } $("#close{{ var_1 }}").click(function (){ document.getElementById("audioplayer").pause() {% for song in songs %} $("#playbtn{{ var_1 }}_{{ var_2 }}").show() $("#pausebtn{{ var_1 }}_{{ var_2 }}").hide() tapid_cache = '' playlist_cache = '' {% endfor %} }) } </script>
let element = location.href.split(',',2).at(1)
<script> let num = 0, tid; const btn = window.document.getElementById("") // 触发事件 btn.onclick = function(e){ triggerEvent() } // 鼠标抬起时 btn.onmousedown = function(e){ let hold_time = 500 // 设置定时,触发事件 tid = setInterval(function(){ triggerEvent() }, hold_time) } // 鼠标移开时,清除计时器 btn.onmouseup = function(e){ clearInterval(tid) } btn.onmouseout = function(e){ clearInterval(tid); // 清除计时器 } // 触发事件 function triggerEvent() { num ++; // 当点击若干秒后执行操作 let action_duration = 5 if (num > action_duration) { btn.innerHTML = num $.ajax({ type: '', url: '', data: {}, success: function(){ window.location.href = '' }, }) } } </script>
// 失焦
$("#id").blur('input',function (){})
// 聚焦
$("#id").focus('input',function (){})
// 开始输入
$("#id").on('input',function (){})
Views
# 通过HttpResponse返回值
if request.method == 'POST':
msg = {
pass_value = 'hello'
}
return HttpResponse(json.dumps(msg), content_type="application/json")
JQuery
<script src="../static/js/jquery-3.5.1.min.js"></script>
<script>
$.ajax({
type:"post",
url :"",
data:{"":""},
success: function (msg){
alert(msg['pass_value')
}
})
</script>
<!-- 在外层添加样式,解决Safari圆角加载动画后失效问题 -->
style = "-webkit-transform:rotate(0deg)"
<!-- 禁止按钮 -->
$("#button").attr('disabled', 'disabled')
<!-- 开启按钮 -->
$("#button").attr('disabled', false)
<!-- 延迟开启按钮 -->
t = setTimeout(function () {
$("#button").attr('disabled', false)
}, 1200);
# views.py 定义接口 from django.shortcuts import render from django.http.response import HttpResponse import json # def playmusic(requst): # if requst.method == 'GET': # result = {} # musicNFT = requst.GET.get('musicNFT') # result['musicNFT'] = musicNFT # result = json.dumps(result) # return HttpResponse(result) # else: # return render(requst,'playmusic.html') def playmusic(requst): if requst.method == 'POST': result = {} musicNFT = requst.GET.get('musicNFT')) result['musicNFT'] = musicNFT result = json.dumps(result) return HttpResponse(result) else: # 此处可对返回值做自定义函数处理 return render(requst,'playmusic.html')
…
# setting.py 定义接口
from django.contrib import admin
from django.urls import path
urlpatterns = [
...
path('playmusic/', views.playmusic),
...
]
…
创建基础页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="/playmusic" method="POSTS">
<h1>用户名:<input name="musicNFT"></h1>
<input type="submit" value="提交">
</form>
</body>
</html>
用户登录展示
{% if user.is_authenticated %}
<span> {{user.username}} </span>
{% endif %}
…
# project/terminal输入
python3.8 manage.py
asj!~fh%$#@#!
[xx/Jun/2022 14:20:26] "GET /xxxxx = HTTP/1.1" 3xx x
# 退出项目
control + c
返回用户登录名
用户没有登陆的时返回AnonymousUser(匿名用户)
作用:
session里设置数值,便于日后访问网页时做判断
方法:
1. 通过request.session[name]=value 【设置数值】
2. 通过request.session.get(name) 【读取数值】
3. 通过request.seesion.set_expire(value)【过期】
实现路径
// 想做一个音乐收藏功能,搞了好几天,终于搞定
// Ugly but it works ...
1. Sqlite数据存储 (ADD/DELETE)
2. DjangoAPI (POST/GET)
3. HTML前端设计 (UI/SVG)
4. AJAX交互 (传参/.CSS()/刷新)
5. 用户体验优化 (卡顿/for循环排序...)
model.py
# 建立收藏数据模型
class Favorite(models.Model):
from datetime import datetime
user_id = models.ForeignKey(to=User, on_delete=models.CASCADE) # 谁收藏
song_id = models.ForeignKey(to=Music, on_delete=models.CASCADE) # 收藏了哪首
collectdate = models.DateTimeField(default=datetime.now) # 收藏时间
class Meta:
verbose_name = _(u'Favorite')
verbose_name_plural = _(u'Favorite')
ordering=['-collectdate']
migrate
# 一系列操作... 大致如下:
# 通知admin
1. admin.site.register(Favorite)
# 数据库建档迁移
2. makemigrations & migrate
...
view.py
@already_login # 要求用户登录 def handle(request): import json from MusicDatabase.models import Favorite from datetime import datetime user_id = request.user.id # 调取正在收藏的用户信息 if request.method == 'POST': song_id = request.POST.get('song_id') # 刚才收藏的歌曲id request.session["song_id"] = song_id # 记录刚才收藏的歌曲id备用 favorites = Favorite.objects.filter(user_id=user_id) # 找出所有该用户的收藏 if favorites.filter(song_id=song_id): # 检查是否已收藏 favorites.filter(song_id=song_id).delete() # 若已收藏夹则取消收藏 request.session["result"] = { # 返回参数 "collect":False, # 歌曲最终未被收藏 "song_id":song_id # 被收藏的歌曲 } print('[取消收藏]') else: collection = Favorite() collection.user_id_id = user_id # 刚才收藏的用户id collection.song_id_id = song_id # 刚才收藏的歌曲id collection.collectdate = datetime.now() # 刚才收藏的时间 collection.save() # 记录收藏信息 request.session["result"] = { "collect": True, "song_id": song_id } print('[新增收藏]') result = json.dumps({"":""}) # 必须正确返回json后进入GET return HttpResponse(result, content_type="application/json") elif request.method == 'GET': # 返回GET结果 result = json.dumps(request.session["result"]) return HttpResponse(result,content_type="application/json") else: # 请求类型检查(大小写/拼写) print('request method error')
SVG绘制与CSS
{# ❤️ } {% for song in msg %} <style> svg { cursor: pointer; overflow: visible; width: 36px; fill:#AAB8C2; fill-rule: evenodd; } </style> <svg id="{{ song.id }}" viewBox="467 392 58 57" xmlns="http://www.w3.org/2000/svg" > <g transform="translate(467 392)"> <path d="M29.144 20.773c-.063-.13-4.227-8.67-11.44-2.59C7.63 28.795 28.94 43.256 29.143 43.394c.204-.138 21.513-14.6 11.44-25.213-7.214-6.08-11.377 2.46-11.44 2.59z" id="{{ song.id }}" /> </g> </svg> {% endfor %}
jQuery - Ajax 交互
// 为避免混乱,POST收藏操作成功获得“返回参数”后再继续GET <script src="../static/js/jquery-3.5.1.min.js"></script> <script> $(function (){ $("#Tag").click(function (){ // 收藏按钮点击后操作 $.ajax({ // 发送操作POST type : "post", // 小写 url : post_url, data : {"key": "value"}, dataType : "json", success:function () { // 成功返参后获取页面收藏信息GET $.ajax({ type : "get", // 小写 url : get_url, data: "", success:function (data) { if (data["collect"] == false){ // 若取消收藏 if (data["song_id"]=={{ song.id }}) { // 通过id锁定 $('#{{ song.id }}') .css({fill: "#AAB8C2", }) // 灰色爱心 } } else { if (data["collect"] == true) { // 若新增收藏 if (data["song_id"]=={{ song.id }}){ // 通过id锁定 $('#{{ song.id }}') .css({fill:"#E2264D",}) // 红色爱心 } } else { alert('ERROR') } } } }) } }); }) //<进入页面时刷新收藏状态> $(function (){ var li = $({{ fav }}) // 列表一定要$()进行obejct化 for (var i=0;i<li.length;i++) { if ({{song.id}} == li[i]) { $("#{{ song.id }}") .css({ fill:"#E2264D",}) } } }) }) </script>
功能描述
通过"中间件"记录用户数据日志
自定义exclude_urls列表访问列表中的url
通过设置的响应时间阈值(可配置化)
将超过阈值的操作日志进行单独保存
创建中间件
(app-item)/middlewares/LogMiddleware.py
setting.py
// 自定义中间件
MIDDLEWARE += [
'app01.middlewares.LogMiddleware.OpLogs'
]
LogMiddleware.py
import time import json from django.utils.deprecation import MiddlewareMixin from MusicStore.models import AccessTimeOutLogs,OpLogs class OpLog(MiddlewareMixin): __exclude_urls = ['signin/','signup/','signout/'] # 无需记录日志的url名单,如:('index/') def __init__(self, *args): super(OpLog, self).__init__(*args) self.start_time = None self.end_time = None self.data = {} def process_request(self, request): self.start_time = time.time() re_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) # 请求IP x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: re_ip = x_forwarded_for.split(",")[0] # 如果有代理,获取真实IP else: re_ip = request.META.get('REMOTE_ADDR') # 请求方法 re_method = request.method # 请求参数 re_content = request.GET if re_method == 'GET' else request.POST if re_content: re_content = json.dumps(re_content) # 筛选空参数 else: re_content = None # 请求记录 self.data.update( { 're_time' : re_time, # 请求时间 're_url' : request.path, # 请求url 're_method' : re_method, # 请求方法 're_ip' : re_ip, # 请求IP 're_content': re_content, # 请求参数 're_user' : request.user.username, # 操作人(需修改),网站登录用户 # 're_user' : 'AnonymousUser' # 匿名用户测试 } ) def process_response(self, request, response): for url in self.__exclude_urls: # 无需记录页面不记录 if url in self.data.get('re_url'): return response # 响应内容 rp_content = response.content.decode() # 获取响应数据字符串(JSON字符串) self.data['rp_content'] = rp_content # 响应耗时 self.end_time = time.time() access_time = self.end_time - self.start_time self.data['access_time'] = round(access_time * 1000) # 耗时毫秒/ms # 单独记录>3s的请求(可在settings中设置"时间阈值") if self.data.get('access_time') > 3 * 1000: AccessTimeOutLogs.objects.create(**self.data) # 超时操作日志入库 OpLogs.objects.create(**self.data) # 操作日志入库 return response
model.py
class OpLogs(models.Model): id = models.AutoField(primary_key=True) re_time = models.CharField(max_length=32, verbose_name='请求时间') re_user = models.CharField(max_length=32, verbose_name='操作人') re_ip = models.CharField(max_length=32, verbose_name='请求IP') re_url = models.CharField(max_length=255, verbose_name='请求url') re_method = models.CharField(max_length=11, verbose_name='请求方法') re_content = models.TextField(null=True, verbose_name='请求参数') rp_content = models.TextField(null=True, verbose_name='响应参数') access_time = models.IntegerField(verbose_name='响应耗时/ms') class Meta: db_table = 'op_logs' class AccessTimeOutLogs(models.Model): id = models.AutoField(primary_key=True) re_time = models.CharField(max_length=32, verbose_name='请求时间') re_user = models.CharField(max_length=32, verbose_name='操作人') re_ip = models.CharField(max_length=32, verbose_name='请求IP') re_url = models.CharField(max_length=255, verbose_name='请求url') re_method = models.CharField(max_length=11, verbose_name='请求方法') re_content = models.TextField(null=True, verbose_name='请求参数') rp_content = models.TextField(null=True, verbose_name='响应参数') access_time = models.IntegerField(verbose_name='响应耗时/ms') class Meta: db_table = 'access_timeout_logs'
migrate
makemigrations / migrate
时区更改
# setting.py
TIME_ZONE = 'Asia/Shanghai' # 'UTC'
...
...
view.py
def fileManagerUpload(request): if request.method == 'POST': username = request.user.username # 上传用户名 timetag = time.strftime('%Y%m%d%m%s') # 时间标签 try: myFile = request.FILES.get("myfile", None) # 文件内容/格式检查 if not myFile: return HttpResponse('没有要上传的文件') if not os.path.splitext(myFile.name)[1] in [".jpg", ".jpeg"]: # 指定格式 return HttpResponse("请上传指定格式文件") # 建立用户专属文件夹 paths = [os.path.join(settings.MEDIA_ROOT, f'user/'), os.path.join(settings.MEDIA_ROOT, f'user/{username}/'), os.path.join(settings.MEDIA_ROOT, f'user/{username}/playlist/')] for i in paths: if not os.path.exists(i): os.mkdir(i) # 文件保存以时间戳命名 final_path = os.path.join(settings.MEDIA_ROOT, f'user/{username}/playlist/') destination = open(os.path.join(final_path, f'{timetag}.jpg'), 'wb+') imgurl = f'../static/media/user/{username}/playlist/{timetag}.jpg' # 图片地址加入服务器缓存 session_tagadd(request, 'imgpath', imgurl, imgurl) # 保存图片 for chunk in myFile.chunks(): destination.write(chunk) destination.close() # 进度提示 hint = '图片上传成功!' session_tagadd(request, 'imghint', hint, '') return HttpResponse(hint) except: hint = '上传失败,请重新上传' session_tagadd(request, 'imghint', hint, '') return HttpResponse(hint) else: return HttpResponse('')
urls.py
path('fileManagerUpload/', fileManagerUpload,name="fileManagerUpload"),
setting.py
MEDIA_ROOT = os.path.join(BASE_DIR,'(app-item)/(temp)')
html
<p id="progressId">上传进度</p> <script src="../static/js/jquery-3.5.1.min.js"></script> <script> var fileChoose = document.getElementById("file-upload"); fileChoose.onchange = function() { var file = this.files[0]; // files[0]DOM对象 // 文件限制大小检查 if (file.size > 1024 * 1024 * 100) { //100M alert("上传文件不能超过100M"); } var reader = new FileReader(); // 实例化FileReader reader.readAsDataURL(file); // 将文件对象转化为路径对象 reader.onload = function () { // 打开文件 var imgEle = document.getElementById("avatar"); // 图片框id imgEle.src = this.result // 这里的this指reader对象 } var fileObject = document.getElementById("file-upload").files[0]; // 拿到图片文件 //实例化FormData对象,添加数据 data.append(key, value) var data = new FormData(); data.append('myfile', fileObject); data.append('filename', $("#titleinfo").val()); $.ajax({ url: '{% url 'fileManagerUpload' %}', type: 'post', data: data, processData: false, //不进行转码或预处理 contentType: false, //不进行"application/x-www-form-urlencoded"的默认编码处理 success: function () { $("#imghint").text("* 上传成功!") }, error: function (){ $("#imghint").text("* 上传失败,请重新上传") }, // 获得用户上传进度 xhr: function(){ // 获取ajaxSettings中的xhr对象,为它的upload属性绑定progress事件的处理函数 var myXhr = $.ajaxSettings.xhr(); if(myXhr.upload){ // 检查upload属性是否存在 // 绑定progress事件的回调函数 $('#waterprogressId').text(); //清空 myXhr.upload.addEventListener('progress', function(e){ if (e.lengthComputable){ var percent = "上传进度:" + e.loaded/e.total*100 + "%"; $('#waterprogressId').text(percent); } }, false); } return myXhr; //xhr对象返回给jQuery使用 } }), // 清除上传信息缓存,保证同名文件也可以上传 document.getElementById('file-upload').value = '' } </script>
[方法 A]
views
def file_download(request):
filename = '下载显示的文件名'
try:
from django.utils.encoding import escape_uri_path
signed_filename = os.path.basename(file_path)
response = FileResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(filename)) # 可支持中文及大文件下载
return response
except Exception:
raise Http404
html
<button href="{% url 'file_download' %}" id="">下载文件</button>
[方法 B]
js
<a id="" href="" Download=""><button>Download</button></a> <script src="../static/js/jquery-3.5.1.min.js"></script> <script> $.ajax({ type : "get", url : "{% url '' %}", data : { "xx":"", }, success: function(data){ var filelink = data.toString() $("#midi_return").attr("href", filelink) }, error: function(){ alert('获取失败') }, }); </script>
# setting.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'xx/media')
# url.py
from django.conf import settings
from django.urls import re_path
from django.views.static import serve
urlpatterns = [ ...
re_path(r'media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]
# 查看文件夹
import os
def data_view(request):
files = [i for i in os.listdir(settings.MEDIA_ROOT) if not i.startswith('.')]
result = {"files": files}
return HttpResponse(json.dumps(result))
# 页面查看或下载
https://xxx.com/media/xxx.pdf
Linux服务器用户文件创建或上传需要打开权限
1. 报错:[Errno 13] Permission denied
2. 原因:上层文件夹缺少用户写入权限
3. 解决:至少建立一个公开读写文件夹并更改文件夹权限
(app-item)/media ->(777)
常用权限代码参考
-rw------- (600) 只有所有者才有读和写的权限
-rwx------ (700) 只有所有者才有读,写,执行的权限
-rwxr-xr-x (755) 只有所有者才有读,写,执行的权限,同组用户和其他用户只有读和执行的权限
-rwx--x--x (711) 只有所有者才有读,写,执行的权限,同组用户和其他用户只有执行的权限
-rw-rw-rw- (666) 每个人都有读写的权限
-rw-rw-r-- (664) 所有者的权限为可读可写不可执行、所属群组可读可写不可执行、其他人可读不可写不可执行
-rwxrwxrwx (777) 每个人都有读写和执行的权限
-rwxrwx--- (770) 所有者和同组用户有读、写及执行权限,其他用户组没任何权限。
# (app-item)/model.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
wechat = models.CharField(max_length=25, null=True,verbose_name="wechat")
phone = models.CharField(max_length=25, null=True,verbose_name="phone")
# setting.py
INSTALLED_APPS = [ ...
'(App-item)',
]
AUTH_USER_MODEL = '(App-item).User'
$ python3.8 manage.py makemigrations
$ python3.8 manage.py migrate
# (app-item)/admin.py
admin.site.register(User)
# (app-item)/view.py
# 方案一
from django.contrib.auth import get_user_model
User = get_user_model()
# 方案二
from django.contrib.auth.models import User
from (app-item).models import User as User
# 对于objects.all()/objects.filter()
# 必须进行序列化后方可进入json转化
from django.core import serializers
data = Music.objects.all()
json_data = serializers.serialize("json", data)
result = {"key":"vlaue"}
result = json.dumps(result)
return HttpResponse(result, content_type="application/json")
from urllib import parse
username = parse.unquote(request.get_full_path().split('')[1])
# 问题报错:
RuntimeWarning: DateTimeField Draw.drawdate received a naive datetime
# 解决方法:
setting.py
USE_TZ = False
import logging
logger = logging.getLogger('django')
logger.error('Something went wrong!')
…
1. 注册账号(需要注册bussiness账号)
2. 登录开发者页面 developer.paypal.com
3. 选择沙盒账号 sandbox account
4. 选择 default application
5. 查看并复制client_id与client_secret
6. 选择个人账号进行测试,复制沙盒个人账号email
7. pip3 install paypalrestsdk
8. setting
9. View
10. Html
setting.py
INSTALLED_APPS = [
...
'paypalrestsdk',
...
]
# 基于沙箱环境
PAYPAL_TEST = True
# 设置收款的 PayPal 电子邮件账户
PAYPAL_REVEIVER_EMAIL = "xxx@xxx.com"
view.py
paypal_client_id = '' paypal_client_secret = '' def paypal_refund(requset): # 退款 from paypalrestsdk import Sale sale = Sale.find("流水号") # Make Refund API call # Set amount only if the refund is partial refund = sale.refund({ "amount": { "total": "0.01", "currency": "USD"}}) # Check refund status if refund.success(): print("Refund[%s] Success" % (refund.id)) else: print("Unable to Refund") print(refund.error) def paypal_execute(request): if request.method == 'GET': import paypalrestsdk paymentid = request.GET.get("paymentId") # 订单id payerid = request.GET.get("payerId") # 支付者id payment = paypalrestsdk.Payment.find(paymentid) if payment.execute({"payer_id": payerid}): print("Payment execute successfully") return HttpResponse("支付成功") else: print(payment.error) # Error Hash return HttpResponse("支付失败") def paypal_payment(request): import paypalrestsdk paypalrestsdk.configure({ "mode": "sandbox", # sandbox代表沙盒/live代表真实付款 "client_id": paypal_client_id, "client_secret": paypal_client_secret, }) payment = paypalrestsdk.Payment({ "intent": "sale", "payer": { "payment_method": "paypal"}, "redirect_urls": { "return_url": "http://localhost:8000/paypal/pay", # 支付成功跳转页面 "cancel_url": "http://localhost:8000/paypal/cancel/"}, # 取消支付页面 "transactions": [{ "amount": { "total": "0.01", "currency": "USD"}, "description": "test_real"}]}) if payment.create(): print("Payment created successfully") for link in payment.links: if link.rel == "approval_url": approval_url = str(link.href) print("Redirect for approval: %s" % (approval_url)) return redirect(approval_url) else: print(payment.error) return HttpResponse("支付失败") def paypal_pay(request): # 用户支付完返paypal返回内容 back_url = request.get_full_path() if 'paymentId=' and 'token=' and 'PayerID=' in back_url: paymentid = back_url.split('paymentId=')[1].split('&token=')[0] token = back_url.split('&token=')[1].split('&PayerID=')[0] payerid = back_url.split('PayerID=')[1] pay = { 'paymentid': paymentid, 'token': token, 'payerid': payerid, } else: pay = {} return render(request, 'paypal_pay.html', pay)
html
<button id="paypal_pay">Pay</button> <button id="paypal_back">Finish</button> <script src="../static/js/jquery-3.5.1.min.js"></script> <script> // 1. 用户点击支付 $("#paypal_pay").click(function () { $.ajax({ type: 'post', url: '{% url 'paypal_payment' %}', data: {}, success: function (data) { $("#paypal_back").show() } }) }) // 2. 用户前往paypal支付完后,回调函数确认 $("#paypal_back").click(function () { $.ajax({ type: 'get', url: '{% url 'paypal_execute' %}', data: { 'paymentId': '{{ paymentid }}', 'payerId': '{{ payerid }}', }, success: function (data) { window.location.href = '{% url '' %}' } }) }) </script>
#1. 安装celery
pip3.8 install celery
#2. 安装后端redis
brew install redis
#3. 设置开机自动启动
brew services start redis
# brew services stop redis 关闭
# brew services restart redis 重启
# 在你的Django项目目录下,通常在同settings.py文件的同一级目录中, # 创建一个新的celery.py文件。 # 在celery.py中,定义你的Celery实例: import os from celery import Celery from django.conf import settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings') app = Celery('your_project_name') app.autodiscover_tasks(['your_app_name']) app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
from .celery import app as celery_app
__all__ = ('celery_app',)
# 在Django的settings.py中,添加Celery的配置。
# 以下是一个使用Redis作为消息代理的基本示例:
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'UTC'
# 在你的Django应用目录下(例如myapp)
# 创建或编辑tasks.py文件,并定义你的任务。
from celery import shared_task
@shared_task
def print_one():
print(1)
# 要定期执行任务,你需要为Celery Beat配置一个调度器
# 在settings.py中添加以下代码:
from datetime import timedelta
CELERY_BEAT_SCHEDULE = {
'print-one-every-hour': {
'task': 'your_app_name.tasks.print_one',
'schedule': timedelta(hours=1),
},
}
terminal 执行
# 在命令行中,从你的Django项目目录运行以下两个命令
#(每个命令需要在不同的终端中运行)
# 启动worker
celery -A your_project_name worker --loglevel=info
#启动beat
celery -A your_project_name beat --loglevel=info
启动Django后自动执行
在project下新建一个start_service.py文件
随后将以下代码写进去
#!/usr/bin/env python3 import signal import sys, os def stop_redis(signal_received, frame): """终止Redis的处理器函数""" print('正在停止Redis...') os.system('pkill redis-server') print('正在停止Celery Worker...') os.system('pkill -f "celery -A _keenthemes worker"') print('正在停止Celery Beat...') os.system('pkill -f "celery -A _keenthemes beat"') sys.exit(0) os.system('pkill redis-server') os.system('pkill -f "celery -A _keenthemes worker"') os.system('pkill -f "celery -A _keenthemes beat"') # 设置捕获SIGINT和SIGTERM的处理器 signal.signal(signal.SIGINT, stop_redis) signal.signal(signal.SIGTERM, stop_redis) # 启动Redis os.system("redis-server &") os.system("celery -A _keenthemes worker --loglevel=info &") os.system("celery -A _keenthemes beat --loglevel=info &") while True: signal.pause()
# Terminal中运行代码
# 授权权限
chmod +x start_service.py
# 执行后台
./start_service.py
# tools.py def draw_image(): import os.path from PIL import Image, ImageDraw, ImageFont from django.conf import settings width,height = 794,1123 # A4大小 album_title = "授权书" album_title = enlarge_fontdistance(album_title) # 样式设置 (⚠️ 字体在服务器上需要预先安装) bg_color = '#F5F5F5' # 背景色 fontsize = [30]# 字体大小 fontcolor = ['#?????']# 字体颜色 fontname = ['?.ttf']# 字体样式 words = ['..']# 文字内容 bocname = '../png' bocpath = os.path.join(bocfldpath, bocname) bgimg_path = '../.png' # 背景图片 img_path = os.path.join(settings.BASE_DIR,bgimg_path) img = Image.open(img_path) for t in range(len(words)): # 文字内容 word = words[t] # 样式应用 font = ImageFont.truetype(fontname[t], fontsize[t]) text_coordinate = (250, int(width / 2 - width / 2.5) + t) # 合成图片 img_draw = ImageDraw.Draw(img) img.save(bocpath, quality=100) # 合成文字 (文字一层层覆盖图片) img_draw.text(text_coordinate, word, font=font, fill=fontcolor[t]) img.save(bocpath, quality=100) return bocpath
# view.py
draw_image(
bocfldpath = userlicensefldpath ,
...
timenow = timenow,
)
# tool.py
def file2zip(zip_file, source_file):
import zipfile,os
with zipfile.ZipFile(zip_file, mode='w', compression=zipfile.ZIP_DEFLATED) as zip:
files = [os.path.join(source_file, i) for i in os.listdir(source_file) if not i.startswith('.')]
for file in files:
parent_path,name = os.path.split(file)
zip.write(file, name)
zip.close()
return zip_file
# view.py
zipnames = [f'?.zip']
source_files = [xpath]
for z in range(len(zipnames)):
zipath = os.path.join(settings.BASE_DIR,xpath)
path = file2zip(
zip_file = os.path.join(zipath,zipnames[z]),
source_file = source_files[z],
)
# -*- coding: utf-8 -*- def checkIdcard(idcard): import re Errors = {'error_msg': '* 身份证号码输入不正确!'} area = {"11": "北京", "12": "天津", "13": "河北", "14": "山西", "15": "内蒙古", "21": "辽宁", "22": "吉林", "23": "黑龙江", "31": "上海", "32": "江苏", "33": "浙江", "34": "安徽", "35": "福建", "36": "江西", "37": "山东", "41": "河南", "42": "湖北", "43": "湖南", "44": "广东", "45": "广西", "46": "海南", "50": "重庆", "51": "四川", "52": "贵州", "53": "云南", "54": "西藏", "61": "陕西", "62": "甘肃", "63": "青海", "64": "宁夏", "65": "新疆", "71": "台湾", "81": "香港", "82": "澳门", "91": "国外"} idcard = str(idcard) idcard = idcard.strip() idcard_list = list(idcard) # 15位身份号码检测 if (len(idcard) == 15): if ((int(idcard[6:8]) + 1900) % 4 == 0 or ( (int(idcard[6:8]) + 1900) % 100 == 0 and (int(idcard[6:8]) + 1900) % 4 == 0)): ereg = re.compile( '[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$') # //测试出生日期的合法性 else: ereg = re.compile( '[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$') # //测试出生日期的合法性 if (re.match(ereg, idcard)): return(Errors) else: return(Errors) # 18位身份号码检测 elif (len(idcard) == 18): # 地区校验 try: area[(idcard)[0:2]] except: return (Errors) # 出生日校验 if (int(idcard[6:10]) % 4 == 0 or (int(idcard[6:10]) % 100 == 0 and int(idcard[6:10]) % 4 == 0)): ereg = re.compile( '[1-9][0-9]{5}(19[0-9]{2}|20[0-9]{2})((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$') # //闰年出生日期的合法性正则表达式 else: ereg = re.compile( '[1-9][0-9]{5}(19[0-9]{2}|20[0-9]{2})((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$') # //平年出生日期的合法性正则表达式 # 出生日期的合法性 if (re.match(ereg, idcard)): # 计算校验位 S = (int(idcard_list[0]) + int(idcard_list[10])) * 7 + (int(idcard_list[1]) + int(idcard_list[11])) * 9 + ( int(idcard_list[2]) + int(idcard_list[12])) * 10 + ( int(idcard_list[3]) + int(idcard_list[13])) * 5 + ( int(idcard_list[4]) + int(idcard_list[14])) * 8 + ( int(idcard_list[5]) + int(idcard_list[15])) * 4 + ( int(idcard_list[6]) + int(idcard_list[16])) * 2 + int(idcard_list[7]) * 1 + int( idcard_list[8]) * 6 + int(idcard_list[9]) * 3 Y = S % 11 M = "F" JYM = "10X98765432" M = JYM[Y] # 判断校验位 if (M == idcard_list[17]): # 检测ID的校验位 region = area[(idcard)[0:2]] year = idcard[6:10] month = idcard[10:12] day = idcard[12:14] if int(idcard[16]) % 2 == 0: sex = '女' else: sex = '男' print('[* 验证通过 *]') print(f'性别:{sex}') print(f'地区:{region}') print(f'出生日期:{year}年{month}月{day}日') return( True, { 'region' : region , 'year' : year , 'month' : month , 'day' : day , 'sex' : sex , } ) else: return(False,Errors) else: return(False,Errors) else: return(False,Errors)
class phoneVertificate(): def __init__(self): # 移动: self.hd_yd = [139, 138, 137, 136, 135, 134, 147, 150, 151, 152, 157, 158, 159, 178, 182, 183, 184, 187, 188] # 联通: self.hd_lt = [130, 131, 132, 155, 156, 185, 186, 145, 176] # 电信: self.hd_dx = [133, 153, 177, 173, 180, 181, 189] # 虚拟运营商: self.hd_xn = [170, 171] self.hd_name = [self.hd_yd, self.hd_lt, self.hd_dx, self.hd_xn] self.hd_strnm = ['中国移动', '中国联通', '中国电信', '虚拟运营商'] self.hd_all = [] self.mobile3All() def mobile3All(self): for yys in self.hd_name: self.hd_all.extend(yys) def verificate(self, mobile: str): if len(mobile) != 11: msg = { 'error_msg': '手机号码输入不正确', 'history': mobile } return (False,msg) try: hd, wh = int(mobile[:3]), int(mobile[3:]) except Exception as err: print(mobile, str(err)) else: if hd not in self.hd_all: msg = { 'error_msg': '手机号码输入不正确', 'history' : mobile } return (False,msg) else: operation = '' for i in range(len(self.hd_name)): if hd in self.hd_name[i]: operation = self.hd_strnm[i] break print(f'验证成功:[{operation}] {mobile}') msg = { 'operation': operation, 'history' : mobile } return (True,msg)
规则条件:密码必须包含字母与数字,8<密码长度<20位
<script src="../static/js/jquery-3.5.1.min.js"></script>
<script>
$("#password").on('input',function(){
var reg = new RegExp(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,20}$/);
var idValue = $("#password").val()
if(reg.test(idValue)){
...
}
})
</script>
也可在html的input里面对最大长度限制
<input type="tel" maxlength="20" >
…
# 阿里云购买轻量服务器
地址: https://www.aliyun.com/
配置: 2核 - 1GB内存 - 系统盘 40GB ESSD - 上海
价格: 80/月
镜像: Linux CentOS 8.2
# 阿里云购买轻量服务器
1. 进入阿里云控制台
2. 设定管理员密码
3. 查看公网/内网地址 (可外网访问)
4. 防火墙打开:8000(Django)/:8888(宝塔)等端口
5. 开启服务器
# 4. 硬盘挂载
…
…
购买云服务器
1. 注册AWS
2. 完成Billing Account 信用卡信息
3. 选择免费服务
4. EC2 创建实例(Linux 免费)
5. 扩充空间 EC2-Elastic Block Store 原始(8G)->(20G)
6. 搜索ssl并添加负载均衡 (参考文章:亚马逊云的服务器(EC2)+阿里云的域名,添加ssl证书)
7. 复制CNAME和VALUE用于域名解析
8. Connect连接实例
9. 安装宝塔
10. Security Group安全组增加访问端口
11. 登录面板
10.安装python管理器2.0
由于宝塔仅支持Centos系统,亚马逊是AmazonLinux
难免不兼容,所以不同版本多装几次(2.1版本经常报错)
我的顺序是3.79->3.71->3.85...
宝塔安装
1. 打开所有端口
2.远程登录,输入sudo -i
3.yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh
SSH 登录
1、连接EC2服务器
2、sudo passwd root # 创建root的密码,输入
3、****** # 提示输入new password。输入一个你要设置的root的密码再验证
4、su root # 接下来,切换到root身份,输入
5、vim /etc/ssh/sshd_config
< PasswordAuthentication yes | PermitRootLogin yes >
# shell 输入按“i”键
# shell 保存退出按“esc" + ":" + "wq" + "enter"
# 使用root身份编辑亚马逊云主机的ssh登录方式找到PasswordAuthentication no,把 no改成yes。PermitRootLogin这行改为PermitRootLogin yes//设定是否允许root管理员直接登录输入:`vim /etc/ssh/sshd_config`
6、systemctl restart sshd # 重启 sshd 服务
7、systemctl enable sshd # 将 sshd 服务加入到开机启动项中
* SHA256 pub/private key ==========
要将SSH密钥添加到Google云平台(GCP)上的实例,你可以按照以下步骤进行操作:
在本地计算机上生成SSH密钥对。如果你已经有SSH密钥对,可以跳过此步骤。否则,可以使用以下命令生成密钥对:
1. ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 这将生成一个RSA类型的4096位密钥对,并将其关联到你的电子邮件地址。
2. Compute Engine => VM instances Edit => SSH => Additem => copy PublicKey(cat ~/.ssh/id_rsa.pub)=>Save
3. 打开本地Privatekey复制
4. 输入host,加载privatekey文件,端口22 => 设置密码
宝塔Django报错:
问题:Django - deterministic=True requires SQLite 3.8.3 or higher upon running python manage.py runserver
1. 安装pysplite3
# 非虚拟环境
pip3 install pysqlite3
pip3 install pysqlite3-binary
# 虚拟环境
请把pip3替换成: 项目路径/md5命名的文件夹/bin/pip
如:/data/python/d9036cc6563924cf9e1da4e1cd64f9a4_venv/bin/pip install pysqlite3
2. 找到报错提示base路径
如:python3.x/site-packages/django/db/backends/sqlite3/base.py
3. 修改替换pysqlite3
# from sqlite3 import dbapi2 as Database # annotation
from pysqlite3 import dbapi2 as Database # import pysqlite3
…
* GoogleCloud删除Project时一定先关闭instance和billing
* 若误删Project,7日内可去IAM&Admin中恢复
====== Linux编译安装python ====== 1.在liunx上安装python运行环境 yum -y install gcc yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel 2.进入到安装目录 cd /usr/local/ # /www/server/panel/pyenv/lib/ 3.下载python3.8的安装包 (可能会有点慢) wget https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz 4.解压安装包 tar -zxvf Python-3.8.1.tgz 5.配置python环境以及安装编译python 1) mkdir /usr/local/python3 # /www/server/panel/pyenv/lib/python3.8 2) cd Python-3.8.1 3) ./configure --prefix=/usr/local/python3 # /www/server/panel/pyenv/lib/python3.8 4) make && make install 6.确认安装成功 /usr/local/python3/bin/python3.8 会出现: Python 3.8.1 (default, Jun 6 2022, 11:01:13) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> print("Hello") Hello >>> exit() 表示安装成功~
====== 运行Django ======
1. 安装Django
/usr/local/python3/bin/python3 install django
2. 安装其他需要组件module
3. 前往project所在目标地址
cd /www/wwwroot/hellojune
4. /usr/local/python3/bin/python3 manage.py runserver 0.0.0.0:80 --insecure
5. 若希望一直挂载开启服务器运行Django
nohup /usr/local/python3/bin/python3 manage.py runserver 0.0.0.0:80 --insecure > django.log 2>&1 &
* Terminal返回一串终端号,如:[5149] 记住然后停止时kill
* > django.log 2>&1 & 用于替换之前nohup.out log位置
* 结束Django nohup使用 kill 5149(之前创建nohup时提示的PID)
MacOS安装 Royal-TSX
1. 使用Royal-TSX代替XShell 远程连接服务器SSH/FTPS
2. Plugins添加Terminal/File Transfer两款插件
3. 新建一个Terminal窗口并输入对应的服务器的信息
4. 指定用户名和密码
5. 完成登录
注:Royal-TSX 图片内容均引用自作者zoiiiiii
服务器下载宝塔脚本(CentOS)
yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec
yum报错处理
存在问题
Question: [服务器yum下载报错]
- Errors during downloading metadata for repository ‘appstream’:
- Status code: 404 for ... Error: Failed to download metadata
- for repo ‘appstream’: Cannot download repomd.xml: Cannot
- download repodata/repomd.xml: All mirrors were tried
...
替换数据源
cd /etc/yum.repos.d
mv CentOS-Linux-BaseOS.repo CentOS-Linux-BaseOS.repo.backup
wget -O CentOS-LinuxBaseOS.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
vim修改文件
vim /etc/yum.repos.d/CentOS-Linux-AppStream.repo
替换baseurl数据源地址
baseurl=http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/AppStream/$basearch/os/
重新创建元数据
yum makecache
安装成功
宝塔Web端登录
1. MacOS浏览器中输入服务器中提示的登录信息
2. 注册宝塔会员并一键安装
1. 腾讯云 -> 申请免费SSL
2. 腾讯云 -> 下载SSL文件
3. 腾讯云 -> 安全组开启443端口
4. 宝塔面板 -> 网站 -> 设置 -> SSL -> 粘贴(key/pem)
-> 证书夹 -> 部署SSL
5. 验证 http -> https
1. 确认本地网络是否为(真)公有ip(查看路由器LanIP与WanIP是否一致)
若不一致则属于NAT模式,需致电运营商客服,改为公有ip。话术:监控需要
2. 致电运营商客服报修,将光猫更改为PPOE拨号模式
3. 购买华硕官方固改路由器 wifi-6
4. 登录后台设置PPOE拨号、DDNS、DMZ、外网
5. 手机断网连接测试是否成功
# 进入目录
cd /abc/
# 查看文件夹里的文件 | df -h
du -sh *
# 查看是/home/文件下那个文件比较大
du --max-depth=1 -h /abc/
# 删除文件夹
rm -rf abc
# 清空日志
>access_log
依赖环境打包
# 项目所在文件夹terminal输入
pip freeze > requirements.txt
# 也可以只打包需要用到的有效插件
pip install pipreqs
pipreqs /path/to/your/project
静态文件打包
python manage.py collectstatic
项目文件打包
将项目文件夹移动至云服务器内
安装python项目管理器
1. 服务器宝塔面板
2. 软件商店
3. 应用搜索“python”
4. 安装“Python项目管理器”
安装python环境
1. 打开Python项目管理器
2. 选自一个与自己项目匹配的python环境进行安装
启动Django项目
1. python管理器添加项目
2. 修改配置 # (可以不设置)
3. 开启映射 #(公网ip/失败的话多试几次)
4. 输入ip+端口进行外网连接测试
Q & A
A. 端口被占用
# 端口被占用报错 bind(): Address already in use [core/socket.c line 769])
sudo fuser -k 8000/tcp # (8000需要填你的端口)
B. SQLite3.9.0 or later is required(found%s)…报错
# 原因: SQLite版本太低,更新或关闭报错
1. 降低Django版本 django==3.0.4
2. 更改设置
C. 单独安装python模块插件
# 因为pip和python所在位置为虚拟环境
在命令行输入 : /项目路径/md5命名的文件夹/bin/pip install xxx
示例:/data/python/d9036cc6563924cf9e1da4e1cd64f9a4_venv/bin/pip install xxx
<uwsgi.ini> 中添加static路径
static-map = /static=/www/wwwroot/(项目名|可替换)/collect_static
注:collect_static需与前期setting设置中STATIC_ROOT一致
静态素材缺失卡了整整一天,试了很多方式包括调整反向代理location,
调整setting等均无效果。最终无意在处理no Internet serve问题时获得答案
总结下来就是不要放弃寻找心中的答案!
安全起见在项目打包上传宝塔前于MacOS环境下完成static素材收集
测试成功显示静态素材
…
PS:网站名申请若有疑问,腾讯会电话申请人并给到一些建议
记得要精准核对电话中的中文字,以免产生误解
…
支付宝支付参考文章
cnblogs.com/xiaolu915/p/10528155.html
…
<script> async function write_to_Dapp() { const contractABI = '[{...}]' const contractAddress = "0x7f33..."; const contract_privatekey = "77599fb..." const info_returns = 'http://...' const provider = new ethers.providers.Web3Provider(window.ethereum) const signer = new ethers.Wallet(contract_privatekey, provider); const contract = new ethers.Contract(contractAddress, contractABI, signer); const accounts = await provider.send("eth_requestAccounts", []); // mint contract const instance = await contract.mint(accounts[0], info_returns) console.log('song mint success', instance) // send transaction // const tx = { // to: accounts[0], // value: ethers.utils.parseEther("0.002"), // type: 1, // gasPrice: await provider.getGasPrice(), // gasLimit: ethers.utils.hexlify(21000), // }; // signer.sendTransaction(tx) // // let varGasPrice = ethers.getGasPrice(); // console.log(varGasPrice, '...') // console.log(parseInt(await provider.getGasPrice())) // const sendTransactionPromise = await signer.sendTransaction(tx); // console.log(sendTransactionPromise) } </script> <script src="../static/js/ethers-5.2.umd.min.js" type="application/javascript"></script>
构建项目
myproject/
│
├── myproject/
│ ├── __init__.py
│ └── main.py
│
├── README.md
└── setup.py
1.1 setup.py
from .myproject import *
# VERSION = '1.0'
# print("Initializing mypackage...")
1.2 setup.py
# setup.py from setuptools import setup, find_packages setup( name='myproject', version='0.1.0', packages=find_packages(), install_requires=[ # 添加你的依赖项 ], entry_points={ 'console_scripts': [ 'myproject = myproject.__main__:main' ] }, author='Your Name', author_email='your.email@example.com', description='Description of your project', url='https://github.com/yourusername/myproject', keywords=['python', 'example'], )
打包
# 1. 打包程序
python setup.py sdist bdist_wheel
# 2. 上传pypi
pip install twine
twine upload dist/*
# 3. 输入token
PyPi官网获取Token,并输入
## 备注
pypi不允许同样的名字和版本号发布
记得产看并清楚不必要的dist里的文件
#安装 pip install jupyterlab #特别版本 pip install "jupyterlab~=3.6.0" # 创建用户(避免root用户直接使用) adduser yourname # 切换用户 su - yourname # 卸载 pip uninstall jupyterlab # 启动//记得打开防火墙8888 jupyter lab --ip='*' --port=8888 # 后台不间断启动 nohup jupyter lab --ip='*' --port=8888 --no-browser > jupyterlab.log 2>&1 & # 调整notebook所在目标文件夹 --notebook-dir=目标路径 #综合命令 nohup jupyter lab --ip='*' --port=8888 --notebook-dir=/home --allow-root # 查看token jupyter server list
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。