Platform visualisasi data sisi web berdasarkan data perayap laba-laba gabungan, akses online
Sumber data berasal dari proyek Joint-spider
Bagian belakang: Django-restframework
Bagian depan: 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/
Detail daftar perumahan komunitas
Cari halaman properti
Halaman detail properti tertentu
Di sekitar rumah
# 项目依赖
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
Proses dasar
Siklus hidup Django:
Ujung depan mengirimkan permintaan-->wsgi Django-->middleware-->sistem perutean-->tampilan-->Operasi basis data ORM-->templat-->mengembalikan data ke pengguna
siklus hidup kerangka istirahat:
Ujung depan mengirimkan permintaan-->wsgi Django-->Middleware-->Routing system_Execute as_view() CBV, yaitu untuk mengeksekusi metode dispath internal-->Sebelum mengeksekusi dispath, ada analisis versi dan penyaji--> Di dispath, merangkum permintaan-->Versi-->Otentikasi-->Izin-->Batasi saat ini-->Tampilan-->Jika tampilan menggunakan caching (request.data atau request.query_params ) menggunakan parser --> tampilan memproses data dan menggunakan serialisasi (serialisasi atau verifikasi data) --> tampilan mengembalikan data menggunakan paging.
Pohon file proyek:
# 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 " ,
}
}
}
Catatan: Proyek ini merupakan pengembangan antarmuka dan tidak melibatkan templat templat dan sumber daya statis, jadi tidak perlu mengonfigurasi kedua item ini.
Masukkan HouseAnalysisHouseAnalysisurls.py dan konfigurasikan informasi perutean:
Tambahkan path('xadmin/', xadmin.site.urls)
, re_path(r'docs/', include_docs_urls(title="HA"))
ke daftar urlpatterns
, xadmin adalah perutean halaman manajemen,
Docs adalah perutean dokumen antarmuka, yang dapat diakses melalui http://localhost:8000/docs/, seperti yang ditunjukkan di bawah ini:
Desain Model Django adalah salah satu dari lima desain inti dasar Django (Desain model, konfigurasi URL, Penulisan tampilan, Desain templat, Dari penggunaan), dan juga merupakan tautan penting dalam model MVC (MVT).
Model adalah model data, bukan basis data. Ini menggambarkan struktur data dan hubungan logis di antara mereka. Di Django, desain Model pada dasarnya adalah sebuah kelas. Setiap model dipetakan ke tabel database.
Masukkan HouseAnalysishousemodels.py dan tambahkan model Api,Elevator,Floor,Layout,Region,Decortion,Purposes,Orientation,Constructure,Community,CommunityRange
untuk menyediakan data untuk serialisasi.
Setelah model dibuat, lakukan migrasi data:
# 进入项目根目录
python manage.py makemigrations
# 同步至数据库
python manage.py migrate
Buat file baru serializer.py di folder rumah untuk membuat serial data.
Definisikan serializer (pada dasarnya sebuah kelas), yang umumnya mencakup bidang kelas model dan memiliki aturan jenis bidangnya sendiri. Setelah mengimplementasikan serializer, Anda dapat membuat objek serial dan kumpulan kueri untuk operasi serialisasi, dan memperoleh data melalui objek serial.data (Anda tidak perlu membuat kamus sendiri lalu mengembalikan data Json)
Saat digunakan untuk serialisasi, teruskan objek kelas model ke dalam parameter instance
Saat digunakan untuk deserialisasi, teruskan data yang akan dideserialisasi ke dalam parameter data .
Selain parameter instance dan data, saat membuat objek Serializer, Anda juga dapat menambahkan data tambahan melalui parameter konteks .
Di sini kita menggunakan serializer model (ModelSerializer), yang sama dengan Serializer biasa, tetapi menyediakan:
Misalnya: Konten rumah dapat diserialkan menggunakan kode sederhana
class HouseSerializer ( serializers . ModelSerializer ):
class Meta :
# 需要序列化的model
model = Api
# 需要序列化的字段
fields = "__all__"
Dengan cara yang sama, serialisasi model yang tersisa, termasuk ElevatorSerializer,FloorSerializer,LayoutSerializer,RegionSerializer,RegionS,CommunitySerializer,CommunityRangeSerializer,DecortionSerializer,PurposesSerializer,ConstructureSerializer,OrientationSerializer
, di antaranya serializer RegionS
berfungsi sebagai serializer unggul CommunitySerializer
dan CommunityRangeSerializer
.
Fungsi tampilan: Terus terang, untuk menerima permintaan front-end dan melakukan pemrosesan data.
(Pemrosesan di sini meliputi: jika front-end adalah permintaan GET, buat kumpulan kueri dan kembalikan hasilnya. Proses ini adalah serialisasi ; jika front-end adalah permintaan POST, jika Anda ingin melakukan perubahan pada database, Anda perlu mendapatkan data yang dikirim oleh front - end. Verifikasi dan tulis data ke database.
Tampilan paling orisinal dapat mengimplementasikan pemrosesan logis seperti itu, tetapi untuk permintaan yang berbeda, beberapa metode perlu didefinisikan dalam tampilan kelas untuk mengimplementasikan pemrosesannya sendiri. Ini dapat menyelesaikan masalah, tetapi ada kekurangannya, yaitu metode umum di setiap fungsi Logikanya serupa: membaca permintaan, mengambil data dari database, menulis sesuatu ke database, dan mengembalikan hasilnya ke front end. Ini akan menghasilkan banyak kode duplikat.
Dalam pandangan pengembangan REST API, meskipun operasi data spesifik dari setiap tampilan berbeda, proses implementasi penambahan, penghapusan, modifikasi dan pemeriksaan pada dasarnya bersifat rutin, sehingga bagian kode ini juga dapat digunakan kembali dan disederhanakan untuk menulis:
Ditambahkan : Verifikasi data permintaan -> Jalankan proses deserialisasi -> Simpan database -> Serialisasi dan kembalikan objek yang disimpan
Delete : Menentukan apakah data yang akan dihapus ada -> melakukan penghapusan database
Modifikasi : Tentukan apakah data yang akan dimodifikasi ada -> verifikasi data yang diminta -> lakukan proses deserialisasi -> simpan database -> serialisasi dan kembalikan objek yang disimpan
Kueri : Kueri database -> Membuat serial dan mengembalikan data
Tampilan ditulis dalam file views.py, dan di sini kami menggunakan diagram pengenalan kelas untuk pengembangan.
http://127.0.0.1/api/v1/allhouse
, kita perlu mengembalikan dan mengekspos hasil serial dari semua informasi rumah.http://127.0.0.1/api/v1/allhouse
/1, kita perlu mengubah id rumah (id di kali ini dapat dikonfigurasi sebagai Tentukan parameter persyaratan) dan hasil serialisasi 1 dikembalikan dan diekspos.{id:xxx,values:xxx...}
, kita perlu memeriksa informasi terkini dan menambahkan data terkini ke dalamnya. database jika itu legal.contoh:
# 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
Tampilan lain serupa, termasuk: ElevaorViewSet,FloorViewSet,LayoutViewSet,RegionViewSet,CommunityViewSet,CommunityPagination,AllCommunityViewSet,CommunityRangeViewSet,DecortionViewSet,PurposesViewSet,ConstructureViewSet,OrientationViewSet
Untuk kelas CommunityViewSet
, kita perlu mengembalikan semua informasinya berdasarkan community_id tertentu. Saat ini, kita tidak dapat menggunakan metode retrieve
dari kelas RetrieveModelMixin
. Kita perlu mengganti metode get_queryset
untuk mengembalikan model tertentu.
def get_queryset ( self ):
if self . request . GET [ 'rid' ] == '0' :
# 获取所有
return Community . objects . all ()
else :
return Community . objects . filter ( region = self . request . GET [ 'rid' ])
Parameter lainnya
Gunakan DefaultRouter
rest_framework
untuk pendaftaran rute:
from rest_framework . routers import DefaultRouter
router = DefaultRouter ()
router . register ( r'v1/api/all_house' , HouseListViewSet , base_name = "house" )
......
Tambahkan router ke urlpatterns:
path ( '' , include ( router . urls )),
Saat ini, ketika permintaan mengakses rute yang ada, rute tersebut akan meneruskan permintaan tersebut ke kelas tampilan. Setelah permintaan diproses di kelas tampilan, hasil serial dikembalikan, yang merupakan data antarmuka yang diekspos oleh backend.
[Transfer gambar tautan eksternal gagal. Situs sumber mungkin memiliki mekanisme anti-leeching. Disarankan untuk menyimpan gambar dan mengunggahnya secara langsung (img-JmH9EULX-1588257851195) (imgs/interface data.png)]
Deskripsi data
Kode status 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
Front-end dibangun menggunakan kerangka dasar Vue dan dikembangkan berdasarkan vue-admin-template. Bisnis front-end utama digunakan untuk tampilan bagan dan informasi peta. Hal ini terutama digunakan untuk memvisualisasikan data harga rumah bekas Chengdu dan informasi terkait, termasuk tampilan informasi keseluruhan, berbagai karakteristik - seperti status dekorasi rumah, visualisasi informasi bagan, pencarian perumahan, visualisasi informasi Sumber perumahan dasar, tampilan peta dan fungsi lainnya.
Deskripsi proyek
Pohon file proyek front-end:
Proses tertentu
# 安装到项目
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 >
Buat folder baru utils di direktori src untuk menyimpan file alat khusus kami, buat request.js baru, dan enkapsulasi ulang metode axios untuk memenuhi operasi saat kami meminta antarmuka. File ini akan mengekspos objek yang digunakan untuk permintaan asinkron sebagai metode akses antarmuka yang lebih canggih.
# 新建一个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
Gunakan vue-router untuk mengimplementasikan fungsi perutean, membuat router.js baru, dan mengonfigurasi item perutean.
// 定义路由列表
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 )
} )
File antarmuka api.js mendefinisikan semua permintaan akses antarmuka yang terkait dengan proyek, dan dienkapsulasi ke dalam fungsi dan diekspos untuk digunakan oleh komponen.
// 例如获取区划信息接口
export function getRegionInfo ( id , params ) {
return request ( {
url : '/region/' + id ,
method : 'get' ,
params
} )
}
Permintaannya adalah objek akses antarmuka yang dienkapsulasi di atas, yang perlu diimpor sebelum digunakan. Saat menggunakan antarmuka dalam komponen, imporlah sesuai permintaan, misalnya:
// charts.js是接口函数文件
import { getRegionInfo } from '@/api/charts'
// 使用
// 第一种方式,由于它本来就是一个promise对象,所以直接在then中获取响应
getRegionInfo ( 1 ) . then ( ( res , err ) => {
// 相关操作
} )
// 第二种方式,使用async await方式,包含await的方法中需要加上async 作为方法前缀
let res = await getRegionInfo ( 1 )