Платформа веб-визуализации данных на основе данных сканера Joint Spider, онлайн-доступ
Источник данных взят из проекта Joint-spider.
Бэкенд: django-restframework
Фронтенд: Vue
# 环境配置需要 python3.7 redis mysql vue vue-cli3.x
#
# 项目启动
# 1. 后端服务启动,进入项目HouseAnalysis
python manage.py runserver
# 2. 前端项目启动,进入项目目录HouseAnalysisWeb
npm run dev
# 3. 前端项目打包
npm run build-prod
# 4. 访问地址:http://localhost:9527/
# 5. 后端接口地址: http://localhost:8000/
Детали списка общественного жилья
Страница поиска недвижимости
Страница сведений о конкретном объекте недвижимости
Вокруг дома
# 项目依赖
Django==2.1.5
django-cors-headers==2.4.0
django-crispy-forms==1.7.2
django-filter==2.0.0
django-formtools==2.1
django-guardian==1.4.9
django-import-export==1.1.0
django-redis==4.10.0
django-reversion==3.0.2
djangorestframework==3.9.0
djangorestframework-jwt==1.11.0
drf-extensions==0.4.0
PyMySQL==0.9.3
redis==3.0.1
SQLAlchemy==1.3.2
Основной процесс
Жизненный цикл Джанго:
Интерфейсная часть отправляет запрос -> wsgi Django -> промежуточное программное обеспечение -> система маршрутизации -> просмотр -> операция с базой данных ORM -> шаблон -> возврат данных пользователю.
Жизненный цикл rest framework:
Интерфейсная часть отправляет запрос --> wsgi Django --> Middleware --> Routing system_Execute CBV as_view(), который должен выполнить внутренний метод dispath --> Перед выполнением dispath выполняется анализ версий и средство рендеринга --> В dispath, инкапсулировать запрос -> Версия -> Аутентификация -> Разрешения -> Ограничить текущий -> Представление -> Если представление использует кеширование (request.data или request.query_params ) использует синтаксический анализатор --> представление обрабатывает данные и использует сериализацию (сериализацию или проверку данных) --> представление возвращает данные с использованием разбиения по страницам.
Дерево файлов проекта:
# 1. 初始化项目
django-admin startproject HouseAnalysis
# 2. 创建相关应用
cd HouseAnalysis
django-admin startapp house
# 项目根目录下创建extra_app 文件夹,存放xadmin压缩文件,运行
pip3 install xadmin
# 1. 配置settings.py 文件 位于\HouseAnalysisHouseAnalysissettings.py
# 找到INSTALLED_APPS,加入配置项
' corsheaders ' ,
' xadmin ' ,
' crispy_forms ' ,
' rest_framework ' ,
' django_filters ' ,
' house '
# 上述配置向是用于将app注册到项目中
# 2. 增加跨域支持
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
' * '
)
CORS_ALLOW_METHODS = (
' DELETE ' ,
' GET ' ,
' OPTIONS ' ,
' PATCH ' ,
' POST ' ,
' PUT ' ,
' VIEW ' ,
)
CORS_ALLOW_HEADERS = (
' XMLHttpRequest ' ,
' X_FILENAME ' ,
' accept-encoding ' ,
' authorization ' ,
' content-type ' ,
' dnt ' ,
' origin ' ,
' user-agent ' ,
' x-csrftoken ' ,
' x-requested-with ' ,
' Pragma ' ,
)
# 3. 在DATABASES中配置MySQL数据库信息
DATABASES = {
' default ' : {
' ENGINE ' : ' django.db.backends.mysql ' ,
' NAME ' : ' house ' ,
' USER ' : ' root ' ,
' PASSWORD ' : ' 123456 ' ,
' HOST ' : ' 127.0.0.1 ' ,
' PORT ' : ' 3306 ' ,
' OPTIONS ' :{
' init_command ' : " SET sql_mode='STRICT_TRANS_TABLES' "
},
}
}
# 4. 配置drf缓存
REST_FRAMEWORK_EXTENSIONS = {
# 10s后失效
' DEFAULT_CACHE_RESPONSE_TIMEOUT ' : 10
}
# 5. 配置redis缓存
CACHES = {
" default " : {
" BACKEND " : " django_redis.cache.RedisCache " ,
" LOCATION " : " redis://127.0.0.1:6379/3 " ,
" OPTIONS " : {
" CLIENT_CLASS " : " django_redis.client.DefaultClient " ,
}
}
}
Примечание. Этот проект представляет собой разработку интерфейса и не задействует шаблоны шаблонов и статические ресурсы, поэтому нет необходимости настраивать эти два элемента.
Введите HouseAnaлизHouseAnaлизurls.py и настройте информацию о маршрутизации:
Добавьте path('xadmin/', xadmin.site.urls)
, re_path(r'docs/', include_docs_urls(title="HA"))
в список urlpatterns
, xadmin — это маршрутизация страницы управления,
Документы — это интерфейс маршрутизации документов, доступ к которому можно получить через http://localhost:8000/docs/, как показано ниже:
Дизайн модели Django — это один из пяти основных основных дизайнов Django (дизайн модели, конфигурация URL-адресов, написание представлений, дизайн шаблонов, использование), а также важное звено в модели MVC (MVT).
Модель — это модель данных, а не база данных. Она описывает структуру данных и логические отношения между ними. В Django проектирование модели — это, по сути, класс. Каждая модель сопоставляется с таблицей базы данных.
Войдите в HouseAnaлизhousemodels.py и добавьте модели Api,Elevator,Floor,Layout,Region,Decortion,Purposes,Orientation,Constructure,Community,CommunityRange
чтобы предоставить данные для сериализации.
После создания модели выполните миграцию данных:
# 进入项目根目录
python manage.py makemigrations
# 同步至数据库
python manage.py migrate
Создайте новый файл Serializer.py в домашней папке для сериализации данных.
Определите сериализатор (по сути класс), который обычно включает поля класса модели и имеет свои собственные правила типов полей. После реализации сериализатора вы можете создавать сериализованные объекты и наборы запросов для операций сериализации, а также получать данные через сериализованный объект object.data (вам не нужно самостоятельно создавать словарь, а затем возвращать данные Json).
При использовании для сериализации передайте объект класса модели в параметр экземпляра .
При использовании для десериализации передайте данные, подлежащие десериализации, в параметр данных .
Помимо параметров экземпляра и данных, при построении объекта сериализатора вы также можете добавить дополнительные данные через параметр контекста .
Здесь мы используем сериализатор модели (ModelSerializer), который аналогичен обычному сериализатору, но обеспечивает:
Например: контент дома можно сериализовать с помощью простого кода.
class HouseSerializer ( serializers . ModelSerializer ):
class Meta :
# 需要序列化的model
model = Api
# 需要序列化的字段
fields = "__all__"
Таким же образом сериализуйте остальные модели, включая ElevatorSerializer,FloorSerializer,LayoutSerializer,RegionSerializer,RegionS,CommunitySerializer,CommunityRangeSerializer,DecortionSerializer,PurposesSerializer,ConstructureSerializer,OrientationSerializer
, среди которых сериализатор RegionS
служит превосходящим сериализатором CommunitySerializer
и CommunityRangeSerializer
.
Функция представления: Грубо говоря, это получение запросов от внешнего интерфейса и выполнение обработки данных.
(Здесь обработка включает в себя: если внешний интерфейс представляет собой запрос GET, создать набор запросов и вернуть результаты. Этот процесс представляет собой сериализацию ; если внешний интерфейс представляет собой запрос POST, если вы хотите внести изменения в базу данных, вам необходимо получить данные, отправленные внешним интерфейсом. Проверить и записать данные в базу данных. Этот процесс называется десериализацией .
Самое оригинальное представление может реализовать такую логическую обработку, но для разных запросов необходимо определить несколько методов в представлении классов, чтобы реализовать собственную обработку. Это может решить проблему, но есть недостаток, то есть общий метод в. Каждая функция. Логика аналогична: запрос чтения, получение данных из базы данных, запись данных в базу данных и возврат результатов во внешний интерфейс. Это приведет к появлению большого количества дублированного кода.
В представлениях разработки REST API, хотя конкретные операции с данными в каждом представлении различны, процесс реализации добавления, удаления, изменения и проверки в основном является рутинным, поэтому эту часть кода также можно использовать повторно и упростить запись:
Добавлено : Проверка данных запроса -> Выполнение процесса десериализации -> Сохранить базу данных -> Сериализация и возврат сохраненного объекта.
Удалить : определить, существуют ли данные, которые нужно удалить -> выполнить удаление базы данных.
Модификация : определить, существуют ли данные, которые необходимо изменить -> проверить запрошенные данные -> выполнить процесс десериализации -> сохранить базу данных -> сериализовать и вернуть сохраненный объект.
Запрос : запросить базу данных -> сериализовать и вернуть данные.
Представления прописываются в файлеviews.py, и здесь для разработки мы используем диаграммы распознавания классов.
http://127.0.0.1/api/v1/allhouse
, нам нужно вернуть и предоставить сериализованные результаты всей информации о доме.http://127.0.0.1/api/v1/allhouse
, нам нужно изменить идентификатор дома (идентификатор по адресу). это время можно настроить как Результат сериализации, который указывает параметр требования) равен 1, возвращается и предоставляется.{id:xxx,values:xxx...}
, нам нужно проверить текущую информацию и добавить текущие данные. базу данных, если это законно.пример:
# CacheResponseMixin: 缓存,内存级别
# ListModelMixin处理GET(all)请求
# RetrieveModelMixin处理(id)请求
# 基础类GenericViewSet,提供分页,过滤,排序等功能
class HouseListViewSet ( CacheResponseMixin , mixins . ListModelMixin , mixins . RetrieveModelMixin , GenericViewSet ):
queryset = Api . objects . all ()
# 房源序列化
serializer_class = HouseSerializer
# 房源分页
pagination_class = HousePagination
# 自定义排序,过滤类
filter_backends = ( DjangoFilterBackend , filters . SearchFilter , filters . OrderingFilter )
# 过滤信息字段
filter_fields = ( 'price' ,)
# 搜搜字段
search_fields = ( 'title' , 'region' , 'community_name' )
# 排序字段
ordering_fields = ( 'price' , 'unit_price' , 'construction_area' )
# 重写retrieve方法,实现获取单个序列化对象信息
def retrieve ( self , request , * args , ** kwargs ):
instance = self . get_object ()
serializer = self . get_serializer ( instance )
return Response ( serializer . data )
# 分页配置项
class HousePagination ( PageNumberPagination ):
page_size = 30
page_size_query_param = 'page_size'
page_query_param = "page"
max_page_size = 100
Другие представления аналогичны, в том числе: ElevaorViewSet,FloorViewSet,LayoutViewSet,RegionViewSet,CommunityViewSet,CommunityPagination,AllCommunityViewSet,CommunityRangeViewSet,DecortionViewSet,PurposesViewSet,ConstructureViewSet,OrientationViewSet
Для класса CommunityViewSet
нам необходимо вернуть всю информацию на основе конкретного Community_id. В настоящее время мы не можем использовать метод retrieve
класса RetrieveModelMixin
. Нам необходимо переопределить метод get_queryset
, чтобы вернуть конкретную модель.
def get_queryset ( self ):
if self . request . GET [ 'rid' ] == '0' :
# 获取所有
return Community . objects . all ()
else :
return Community . objects . filter ( region = self . request . GET [ 'rid' ])
Другие параметры
Используйте DefaultRouter
rest_framework
для регистрации маршрута:
from rest_framework . routers import DefaultRouter
router = DefaultRouter ()
router . register ( r'v1/api/all_house' , HouseListViewSet , base_name = "house" )
......
Добавьте маршрутизатор в шаблоны URL:
path ( '' , include ( router . urls )),
В это время, когда запрос обращается к существующему маршруту, маршрут перенаправляет запрос в класс представления. После обработки запроса в классе представления возвращается сериализованный результат, который представляет собой данные интерфейса, предоставляемые серверной частью.
[Не удалось передать изображение по внешней ссылке. Исходный сайт может иметь механизм защиты от кражи. Рекомендуется сохранить изображение и загрузить его напрямую (img-JmH9EULX-1588257851195) (imgs/interface data.png)]
Описание данных
Код состояния HTTP:
# 将所有房源数,小区数,总体单价价均价,总体总价均价,存入redis
def insertIndexInfo ():
all_number = house . count ()
com_number = Api . objects . values ( 'community_name' ). distinct (). count ()
mean_price = format ( house . aggregate ( Avg ( 'price' ))[ 'price__avg' ], '.3f' )
mean_unit_price = format ( house . aggregate ( Avg ( 'unit_price' ))[ 'unit_price__avg' ], '.3f' )
data = {
"all_number" : all_number ,
"com_number" : com_number ,
"mean_price" : mean_price ,
"mean_unit_price" : mean_unit_price
}
redis_conn . mset ( data )
# 1. 定义专门数据库操作类InsertIntoMysql
class InsertIntoMysql ():
# 属性中定义sql语句获取数据
def __init__ ( self ):
self . elevator_sql = 'select unit_price, price, elevator from house_api'
# ......
# 定义数据插入方法
def insertElevator ( self ):
index , values , price_mean , unit_price_mean = multiple ( self . elevator_sql , engine , 'elevator' )
for i in range ( len ( index )):
elevator = Elevator ()
elevator . version = 'v1'
elevator . title = '电梯分布情况'
elevator . has_ele = index [ i ]
elevator . el_num = values [ i ]
elevator . mean_price = price_mean [ i ]
elevator . mean_unit_price = unit_price_mean [ i ]
elevator . save ()
# 其他剩余数据插入方法
# ......
# 2. 数据处理模块,包括单因子和对因子处理
# 利用数据处理模块pandas
def single ( sql , engine , feature ):
"""
:param sql: sql语句
:param engine: mysql引擎
:param feature: 数据特征
:return: 特征,数量
"""
df_sql = sql
df = pd . read_sql ( df_sql , engine )
df [ feature ] = df [ feature ]. astype ( str )
if feature == 'floor' :
df [ df [ feature ] == '暂无数据' ] = '18'
df [ feature ] = df [ feature ]. apply ( lambda x : re . findall ( 'd+' , x )[ 0 ])
feature_res = df [ feature ]. value_counts ()
index = feature_res . index
values = feature_res . values
return index , values
def multiple ( sql , engine , feature ):
"""
:param sql: sql语句
:param engine: mysql引擎
:param feature: 数据特征
:return: 特征,数量,总价均价,单价均价
"""
df_sql = sql
df = pd . read_sql ( df_sql , engine )
df [ feature ] = df [ feature ]. astype ( str )
if feature == 'region' :
df [ feature ] = df [ feature ]. apply ( lambda x : re . sub ( r"[|]|'" , '' , x ). split ( ',' )[ 0 ])
feature_res = df [ feature ]. value_counts ()
index = feature_res . index
values = feature_res . values
price_mean = []
unit_price_mean = []
max_unit_price = []
min_unit_price = []
for inx , val in zip ( index , values ):
price_mean . append ( format ( df [ df [ feature ] == inx ][ 'price' ]. astype ( float ). mean (), '.3f' ))
unit_price_mean . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). mean (), '.3f' ))
max_unit_price . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). max (), '.3f' ))
min_unit_price . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). min (), '.3f' ))
return index , values , price_mean , unit_price_mean , max_unit_price , min_unit_price
Интерфейс построен с использованием базового фреймворка Vue и разработан на основе vue-admin-template. Основной внешний вид бизнеса используется для отображения диаграмм и картографической информации. Он в основном используется для визуализации данных о ценах на подержанное жилье в Чэнду и соответствующей информации, включая отображение общей информации, различных характеристик, таких как состояние отделки дома, визуализация информации о диаграмме. поиск жилья, базовое жилье. Визуализация исходной информации, отображение карты и другие функции.
Описание проекта
Дерево файлов фронтенд-проекта:
Конкретный процесс
# 安装到项目
npm install -S element-ui
# 全局加载: 在main.js中
import ElementUI from ' element-ui '
import ' element-ui/lib/theme-chalk/index.css '
import locale from ' element-ui/lib/locale/lang/en ' // i18n
Vue.use(ElementUI, { locale })
# 这样我们据可以在vue文件中使用UI组件,如: 按钮组件<Button />
# 安装到项目
npm install -S vue-baidu-map
# 局部加载: 在任意vue文件中
import BaiduMap from ' vue-baidu-map/components/map/Map.vue '
# 地图标注组件
import { BmMarker } from ' vue-baidu-map '
# 使用,例如:
< baidu-map ref= " baidumap "
class= " bm-view "
style= " width:100%;height:93vh "
ak= " YOUR AK "
:center= " {lng: 104.07, lat: 30.67} "
:scroll-wheel-zoom= " true "
:zoom= " 16 " >
< bm-navigation anchor= " BMAP_ANCHOR_TOP_LEFT " ></bm-navigation >
< bm-marker v-for= " spoi in searchResults " -- >
< ! --:position= " {lng: spoi.lng, lat: spoi.lat} " -- >
< ! --:title= " spoi.community_name " > -- >
< /bm-marker >
< /baidu-map >
Создайте новую папку utils в каталоге src для хранения наших настроенных файлов инструментов, создайте новый Request.js и повторно инкапсулируйте метод axios для выполнения операций, когда мы запрашиваем интерфейс. Этот файл предоставит объект, используемый для асинхронных запросов, как более продвинутый метод доступа к интерфейсу. Этот метод будет использоваться при разработке файла интерфейса.
# 新建一个servie对象
const service = axios . create ( {
// 项目基本配置文件中定义的VUE_APP_BASE_API,开发和发布环境各部相同,在.env.devolopment和.env.product文件中进行配置
baseURL : process . env . VUE_APP_BASE_API ,
// 直接写死的url,无论环境如何都不会改变
// baseURL: 'http://127.0.0.1:8000/v1/api',
timeout : 8000 // 最大请求时间
} )
service . interceptors . request . use ( config => {
// 请求方法为POST时,配置请求头
config . method === 'post'
? config . data = qs . stringify ( { ... config . data } )
: config . params = { ... config . params }
config . headers [ 'Content-Type' ] = 'application/x-www-form-urlencoded'
return config
} , error => {
// 错误时提示信息
Message ( {
message : error ,
type : 'error' ,
duration : 3 * 1000
} )
Promise . reject ( error )
} )
service . interceptors . response . use (
response => {
if ( response . statusText === 'OK' ) {
// 正确响应时,直接返回数据
return response . data
} else {
Notification ( {
title : '错误' ,
message : '访问api错误' , // TODO 后端拦截错误请求
type : 'error' ,
duration : 3 * 1000
} )
}
} ,
// 错误处理
error => {
let dtext = JSON . parse ( JSON . stringify ( error ) )
try {
if ( dtext . response . status === 404 ) {
Notification ( {
type : 'error' ,
title : '出问题404' ,
message : '访问api错误或服务器忙' ,
duration : 3 * 1000
} )
}
} catch ( err ) {
Notification ( {
title : '错误' ,
message : '请求超时,请稍后再试或刷新页面' ,
type : 'error' ,
duration : 3 * 1000
} )
}
return Promise . reject ( error )
}
)
export default service
Используйте vue-router для реализации функции маршрутизации, создайте новый router.js и настройте элементы маршрутизации.
// 定义路由列表
export const constantRoutes = [
{
path : '/404' ,
component : ( ) => import ( '@/views/404' ) ,
hidden : true
} ,
{
path : '/' ,
component : Layout ,
redirect : '/dashboard' ,
children : [ {
path : 'dashboard' ,
name : 'Dashboard' ,
// 采用路由懒加载的方式
component : ( ) => import ( '@/views/dashboard/index' ) ,
meta : { title : '房源信息' , icon : 'house' }
} ]
} ,
// ......
]
// 构造路由对象
const createRouter = ( ) => new Router ( {
// mode: 'history', // require service support
scrollBehavior : ( ) => ( { y : 0 } ) ,
routes : constantRoutes
} )
// 在main.js 中引入并挂载
import router from './router'
new Vue ( {
el : '#app' ,
router ,
render : h => h ( App )
} )
Файл интерфейса api.js определяет все запросы доступа к интерфейсу, связанные с проектом, инкапсулируется в функции и предоставляется для использования компонентами.
// 例如获取区划信息接口
export function getRegionInfo ( id , params ) {
return request ( {
url : '/region/' + id ,
method : 'get' ,
params
} )
}
Запрос — это инкапсулированный выше объект доступа к интерфейсу, который необходимо импортировать перед использованием. При использовании интерфейса в компоненте импортируйте его по требованию, например:
// charts.js是接口函数文件
import { getRegionInfo } from '@/api/charts'
// 使用
// 第一种方式,由于它本来就是一个promise对象,所以直接在then中获取响应
getRegionInfo ( 1 ) . then ( ( res , err ) => {
// 相关操作
} )
// 第二种方式,使用async await方式,包含await的方法中需要加上async 作为方法前缀
let res = await getRegionInfo ( 1 )