yh:修改注释后发生的错误

master
云涵 4 weeks ago
parent e080c41f83
commit 64280d764f

@ -1,4 +1,5 @@
import time
import logging
import elasticsearch.client
from django.conf import settings
from elasticsearch_dsl import Document, InnerDoc, Date, Integer, Long, Text, Object, GeoPoint, Keyword, Boolean
@ -6,14 +7,18 @@ from elasticsearch_dsl.connections import connections
from blog.models import Article
logger = logging.getLogger(__name__)
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL') # 是否启用 Elasticsearch
# 如果启用,则建立连接
if ELASTICSEARCH_ENABLED:
connections.create_connection(hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
from elasticsearch.client import IngestClient
c = IngestClient(es)
try:
c.get_pipeline('geoip')
@ -27,6 +32,7 @@ if ELASTICSEARCH_ENABLED:
}
''')
# 定义 IP 地理位置信息内部文档
class GeoIp(InnerDoc):
continent_name = Keyword()
@ -34,19 +40,23 @@ class GeoIp(InnerDoc):
country_name = Keyword()
location = GeoPoint()
# 用户代理(浏览器/设备/操作系统)相关内部类
class UserAgentBrowser(InnerDoc):
Family = Keyword()
Version = Keyword()
class UserAgentOS(UserAgentBrowser):
pass
class UserAgentDevice(InnerDoc):
Family = Keyword()
Brand = Keyword()
Model = Keyword()
class UserAgent(InnerDoc):
browser = Object(UserAgentBrowser, required=False)
os = Object(UserAgentOS, required=False)
@ -54,6 +64,7 @@ class UserAgent(InnerDoc):
string = Text()
is_bot = Boolean()
# 性能监控文档:记录每个请求的 URL、耗时、IP、用户代理等
class ElapsedTimeDocument(Document):
url = Keyword()
@ -67,6 +78,7 @@ class ElapsedTimeDocument(Document):
name = 'performance'
settings = {"number_of_shards": 1, "number_of_replicas": 0}
# 文章搜索文档:用于全文检索
class ArticleDocument(Document):
body = Text(analyzer='ik_max_word', search_analyzer='ik_smart') # 使用IK中文分词
@ -85,4 +97,305 @@ class ArticleDocument(Document):
name = 'blog'
settings = {"number_of_shards": 1, "number_of_replicas": 0}
# Elasticsearch 索引管理工具类(略,见原文档)
# 新增ArticleDocumentManager 类
class ArticleDocumentManager:
"""
ArticleDocument 管理器类
用于处理 Elasticsearch 相关的文章搜索操作
"""
def __init__(self, document_class=ArticleDocument):
self.document_class = document_class
self.es_enabled = ELASTICSEARCH_ENABLED
def search_articles(self, query, category=None, author=None, tags=None, size=10, from_index=0):
"""
搜索文章
"""
if not self.es_enabled:
# 如果 Elasticsearch 未启用,返回空结果
return [], 0
try:
search = self.document_class.search()
if query:
# 多字段匹配搜索,标题权重更高
search = search.query(
"multi_match",
query=query,
fields=[
'title^3', # 标题权重最高
'body^2', # 正文权重次之
'tags.name^2', # 标签权重
'category.name^1.5', # 分类权重
'author.nickname^1' # 作者权重
],
fuzziness="AUTO" # 自动模糊匹配
)
# 应用过滤器
if category:
search = search.filter('term', category__name=category)
if author:
search = search.filter('term', author__nickname=author)
if tags:
if isinstance(tags, str):
tags = [tags]
for tag in tags:
search = search.filter('term', tags__name=tag)
# 只搜索已发布的文章
search = search.filter('term', status='p')
# 获取总数
total = search.count()
# 应用分页
search = search[from_index:from_index + size]
# 执行搜索
response = search.execute()
return response, total
except Exception as e:
logger.error(f"Elasticsearch search failed: {e}")
return [], 0
def get_popular_articles(self, size=10):
"""
获取热门文章按浏览量排序
"""
if not self.es_enabled:
return []
try:
search = self.document_class.search()
search = search.filter('term', status='p')
search = search.sort('-views') # 按浏览量降序
search = search[:size]
response = search.execute()
return response
except Exception as e:
logger.error(f"Failed to get popular articles: {e}")
return []
def get_recent_articles(self, size=10):
"""
获取最新文章按发布时间排序
"""
if not self.es_enabled:
return []
try:
search = self.document_class.search()
search = search.filter('term', status='p')
search = search.sort('-pub_time') # 按发布时间降序
search = search[:size]
response = search.execute()
return response
except Exception as e:
logger.error(f"Failed to get recent articles: {e}")
return []
def get_articles_by_category(self, category_name, size=10):
"""
根据分类获取文章
"""
if not self.es_enabled:
return []
try:
search = self.document_class.search()
search = search.filter('term', category__name=category_name)
search = search.filter('term', status='p')
search = search.sort('-pub_time')
search = search[:size]
response = search.execute()
return response
except Exception as e:
logger.error(f"Failed to get articles by category: {e}")
return []
def get_articles_by_tag(self, tag_name, size=10):
"""
根据标签获取文章
"""
if not self.es_enabled:
return []
try:
search = self.document_class.search()
search = search.filter('term', tags__name=tag_name)
search = search.filter('term', status='p')
search = search.sort('-pub_time')
search = search[:size]
response = search.execute()
return response
except Exception as e:
logger.error(f"Failed to get articles by tag: {e}")
return []
def get_similar_articles(self, article_id, size=5):
"""
获取相似文章基于更多相似项
"""
if not self.es_enabled:
return []
try:
search = self.document_class.search()
# 使用 more_like_this 查询找到相似文章
search = search.query(
'more_like_this',
fields=['title', 'body', 'tags.name', 'category.name'],
like={"_id": article_id},
min_term_freq=1,
max_query_terms=12
)
search = search.filter('term', status='p')
search = search.exclude('term', _id=article_id) # 排除当前文章
search = search[:size]
response = search.execute()
return response
except Exception as e:
logger.error(f"Failed to get similar articles: {e}")
return []
def rebuild_index(self):
"""
重建文章索引
"""
if not self.es_enabled:
return False
try:
# 删除现有索引
self.document_class._index.delete(ignore=404)
# 创建新索引
self.document_class._index.create()
# 重新索引所有文章
self.document_class.init()
logger.info("Article index rebuilt successfully")
return True
except Exception as e:
logger.error(f"Failed to rebuild article index: {e}")
return False
def get_index_stats(self):
"""
获取索引统计信息
"""
if not self.es_enabled:
return {}
try:
from elasticsearch.client import IndicesClient
client = IndicesClient(connections.get_connection())
stats = client.stats(index='blog')
return stats
except Exception as e:
logger.error(f"Failed to get index stats: {e}")
return {}
# 创建全局实例
article_document_manager = ArticleDocumentManager()
# Elasticsearch 索引管理工具类
class ElasticsearchManager:
"""
Elasticsearch 索引管理工具类
"""
def __init__(self):
self.es_enabled = ELASTICSEARCH_ENABLED
def create_all_indices(self):
"""
创建所有索引
"""
if not self.es_enabled:
return False
try:
# 创建文章索引
ArticleDocument.init()
# 创建性能监控索引
ElapsedTimeDocument.init()
logger.info("All Elasticsearch indices created successfully")
return True
except Exception as e:
logger.error(f"Failed to create indices: {e}")
return False
def delete_all_indices(self):
"""
删除所有索引
"""
if not self.es_enabled:
return False
try:
ArticleDocument._index.delete(ignore=404)
ElapsedTimeDocument._index.delete(ignore=404)
logger.info("All Elasticsearch indices deleted successfully")
return True
except Exception as e:
logger.error(f"Failed to delete indices: {e}")
return False
def refresh_all_indices(self):
"""
刷新所有索引
"""
if not self.es_enabled:
return False
try:
from elasticsearch.client import IndicesClient
client = IndicesClient(connections.get_connection())
client.refresh(index='_all')
logger.info("All Elasticsearch indices refreshed successfully")
return True
except Exception as e:
logger.error(f"Failed to refresh indices: {e}")
return False
def get_cluster_health(self):
"""
获取集群健康状态
"""
if not self.es_enabled:
return {}
try:
health = connections.get_connection().cluster.health()
return health
except Exception as e:
logger.error(f"Failed to get cluster health: {e}")
return {}
# 创建全局实例
elasticsearch_manager = ElasticsearchManager()

@ -1,10 +1,67 @@
# 记录每个请求的加载时间、IP、用户代理可选地存入 Elasticsearch
class OnlineMiddleware(object):
import time # 添加这行
import logging
from django.conf import settings
from django.utils import timezone
logger = logging.getLogger(__name__)
class OnlineMiddleware:
"""
在线用户中间件 - 记录每个请求的加载时间IP用户代理可选地存入 Elasticsearch
"""
def __init__(self, get_response=None):
self.get_response = get_response
def __call__(self, request):
# 记录请求开始时间
start_time = time.time()
# 处理请求
response = self.get_response(request)
# 计算耗时,记录并显示到页面
...
duration = time.time() - start_time
# 获取客户端IP
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
# 获取用户代理
user_agent = request.META.get('HTTP_USER_AGENT', '')
# 记录日志
logger.info(
f"Request: {request.method} {request.path} - "
f"IP: {ip} - "
f"Time: {duration:.3f}s"
)
# 可选:存入 Elasticsearch
if hasattr(settings, 'ELASTICSEARCH_DSL') and settings.ELASTICSEARCH_DSL:
try:
from blog.documents import ElapsedTimeDocument
doc = ElapsedTimeDocument(
url=request.path,
time_taken=int(duration * 1000),
log_datetime=timezone.now(),
ip=ip,
useragent={'string': user_agent}
)
doc.save()
except Exception as e:
logger.warning(f"Failed to save to Elasticsearch: {e}")
# 添加处理时间到响应头
response['X-Response-Time'] = f'{duration:.3f}s'
return response
def process_exception(self, request, exception):
"""处理异常"""
logger.error(f"Middleware exception: {exception}")
return None

@ -84,7 +84,7 @@ class DjangoBlogAdminSite(AdminSite):
admin_site = DjangoBlogAdminSite(name='admin')
# 注册博客相关模型到后台管理
admin_site.register(Article, ArticlelAdmin) # 文章模型
admin_site.register(Article, ArticleAdmin) # 文章模型
admin_site.register(Category, CategoryAdmin) # 分类模型
admin_site.register(Tag, TagAdmin) # 标签模型
admin_site.register(Links, LinksAdmin) # 友情链接模型

@ -1,197 +1,56 @@
"""
<<<<<<< HEAD
Django 数据库迁移模块 - OAuth 认证配置
=======
OAuth应用数据库迁移文件
# oauth/migrations/0001_initial.py
本迁移文件由Django自动生成用于创建OAuth认证相关的数据库表结构
包含OAuth配置和OAuth用户两个主要模型支持多种第三方登录方式
生成的表结构
- oauth_oauthconfig: OAuth服务提供商配置表
- oauth_oauthuser: OAuth用户信息表
迁移依赖
- 依赖于Django内置的用户模型AUTH_USER_MODEL
"""
# Generated by Django 4.1.7 on 2023-03-07 09:53
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
该模块用于创建OAuth认证相关的数据库表结构包含OAuth服务提供商配置和OAuth用户信息两个主要模型
这是Django迁移系统自动生成的迁移文件在Django 4.1.7版本中创建于2023-03-07
"""
# 导入Django核心模块
from django.conf import settings # 导入Django设置
from django.db import migrations, models # 导入数据库迁移和模型相关功能
import django.db.models.deletion # 导入外键删除操作
import django.utils.timezone # 导入时区工具
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
"""
<<<<<<< HEAD
OAuth认证系统的数据库迁移类
=======
OAuth应用初始迁移类
继承自migrations.Migration定义数据库表结构的创建操作
initial = True 表示这是该应用的第一个迁移文件
"""
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
这个迁移类负责创建OAuth认证功能所需的数据库表结构
包括OAuth服务提供商配置和第三方登录用户信息存储
"""
# 标记为初始迁移
initial = True
# 定义依赖关系 - 依赖于可切换的用户模型
dependencies = [
# 声明对Django用户模型的依赖
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
# 定义要执行的数据库操作
operations = [
<<<<<<< HEAD
# 创建OAuthConfig模型对应的数据库表
migrations.CreateModel(
name='OAuthConfig',
fields=[
# 主键ID字段自增BigAutoField
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# OAuth服务类型选择字段支持多种第三方登录
('type', models.CharField(
choices=[('weibo', '微博'), ('google', '谷歌'), ('github', 'GitHub'), ('facebook', 'FaceBook'),
('qq', 'QQ')], default='a', max_length=10, verbose_name='类型')),
# OAuth应用的AppKey字段
('appkey', models.CharField(max_length=200, verbose_name='AppKey')),
# OAuth应用的AppSecret字段用于安全认证
('appsecret', models.CharField(max_length=200, verbose_name='AppSecret')),
# OAuth回调地址字段用于接收授权码
('callback_url',
models.CharField(default='http://www.baidu.com', max_length=200, verbose_name='回调地址')),
# 是否启用该OAuth配置的布尔字段
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
# 记录创建时间,默认使用当前时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 记录最后修改时间,默认使用当前时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
],
options={
# 设置模型在Admin中的单数显示名称
=======
# 创建OAuth配置表
migrations.CreateModel(
name='OAuthConfig',
fields=[
# 主键字段 - 使用BigAutoField作为自增主键
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# OAuth类型字段 - 使用选择框限定支持的第三方登录类型
('type', models.CharField(
choices=[('weibo', '微博'), ('google', '谷歌'), ('github', 'GitHub'), ('facebook', 'FaceBook'),
('qq', 'QQ')], default='a', max_length=10, verbose_name='类型')),
# AppKey字段 - 存储OAuth应用的密钥标识
('type', models.CharField(choices=[('weibo', '微博'), ('google', '谷歌'), ('github', 'GitHub'), ('facebook', 'FaceBook'), ('qq', 'QQ')], default='a', max_length=10, verbose_name='类型')),
('appkey', models.CharField(max_length=200, verbose_name='AppKey')),
# AppSecret字段 - 存储OAuth应用的密钥
('appsecret', models.CharField(max_length=200, verbose_name='AppSecret')),
# 回调地址字段 - 默认设置为百度首页
('callback_url',
models.CharField(default='http://www.baidu.com', max_length=200, verbose_name='回调地址')),
# 启用状态字段 - 控制该OAuth配置是否可用
('callback_url', models.CharField(default='http://www.baidu.com', max_length=200, verbose_name='回调地址')),
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
# 创建时间字段 - 自动记录创建时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 最后修改时间字段 - 自动记录最后修改时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
],
options={
# 管理后台显示名称
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
'verbose_name': 'oauth配置',
# 设置模型在Admin中的复数显示名称
'verbose_name_plural': 'oauth配置',
<<<<<<< HEAD
# 设置默认排序字段,按创建时间降序排列
'ordering': ['-created_time'],
},
),
# 创建OAuthUser模型对应的数据库表
migrations.CreateModel(
name='OAuthUser',
fields=[
# 主键ID字段自增BigAutoField
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 第三方平台的用户唯一标识
('openid', models.CharField(max_length=50)),
# 用户在第三方平台的昵称
('nickname', models.CharField(max_length=50, verbose_name='昵称')),
# OAuth访问令牌可为空
('token', models.CharField(blank=True, max_length=150, null=True)),
# 用户头像URL可为空
('picture', models.CharField(blank=True, max_length=350, null=True)),
# OAuth服务类型
('type', models.CharField(max_length=50)),
# 用户邮箱,可为空
('email', models.CharField(blank=True, max_length=50, null=True)),
# 存储额外的元数据信息使用Text字段
('metadata', models.TextField(blank=True, null=True)),
# 记录创建时间,默认使用当前时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 记录最后修改时间,默认使用当前时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
# 外键关联到本地用户模型,建立第三方账号与本地用户的关联
=======
# 默认排序规则 - 按创建时间倒序
'ordering': ['-created_time'],
},
),
# 创建OAuth用户表
migrations.CreateModel(
name='OAuthUser',
fields=[
# 主键字段
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 第三方平台用户唯一标识
('openid', models.CharField(max_length=50)),
# 用户在第三方平台的昵称
('nickname', models.CharField(max_length=50, verbose_name='昵称')),
# OAuth访问令牌 - 可为空
('token', models.CharField(blank=True, max_length=150, null=True)),
# 用户头像URL - 可为空
('picture', models.CharField(blank=True, max_length=350, null=True)),
# OAuth类型 - 标识来自哪个第三方平台
('type', models.CharField(max_length=50)),
# 用户邮箱 - 可为空
('email', models.CharField(blank=True, max_length=50, null=True)),
# 元数据字段 - 存储额外的用户信息JSON格式
('metadata', models.TextField(blank=True, null=True)),
# 创建时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
# 最后修改时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
# 外键关联到本地用户 - 建立第三方账号与本地账号的关联
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL, verbose_name='用户')),
('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')),
],
options={
<<<<<<< HEAD
# 设置模型在Admin中的单数显示名称
=======
# 管理后台显示名称
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
'verbose_name': 'oauth用户',
# 设置模型在Admin中的复数显示名称
'verbose_name_plural': 'oauth用户',
<<<<<<< HEAD
# 设置默认排序字段,按创建时间降序排列
=======
# 默认排序规则
>>>>>>> d4786ee23b15aa002b21504f1056098d46f303c5
'ordering': ['-created_time'],
},
),

Loading…
Cancel
Save