全栈Demo开发流程 Flutter(GetX)+Django(OpenAPI)+Oracle 21c XE(ORDS for restful api)
前端:用户控制(C)、界面展示(V)、与后端数据交互(M)
后端:API接口(C),数据格式化(V),业务逻辑、第三方接口、RPC、本地数据、数据库数据(M)
开发流程
Django→Database→Flutter
Django的Model能迁移至Databse,但反之则需要https://github.com/farhan-cashify/django-inspectdb-refactor
Flutter与Django的通信,通过Restful HTTP请求来做
Django可以渲染备用(手机)HTML页面,第三方API转接
Flutter
https://medium.flutterdevs.com/rest-api-using-getx-c5f40324dfcb
https://www.geeksforgeeks.org/implementing-rest-api-in-flutter/
大一统与分天下的矛盾
看了下Flutter for Web,未来跟wasm的结合,断定Flutter只适合做本地(网页)客户端,不适合做网页,因为与Html+js,html能暴露内容(可读性强),js负责html元素互动
这也能解释搜索引擎某些场景越来越难用,它无法检索微信内部公众号文章、网盘资源、咸鱼二手、拼多多商品……不打算做web内容的平台(平台信息不外散),canvas内部的文字对seo来说也是无法直接读的东西(Flutter-Canvaskit)
性能上肯定是wasm比js快,但二进制代码就是读不懂,也许要妥协的不是flutter、wasm,而是SEO,开发新技术快速解码二进制、读不懂的消息,这又涉及到编译原理与密码学等……
目前来看可以用:服务器渲染“不加载flutter与js的备用html页面”,也就是目前seo爬虫能直接看到的数据,为什么flutter没有那种简单的“组件widget转换html标签”的功能呢?
这也是开源与闭源的矛盾,开源是开放平台,闭源是封闭平台
专业的事情交给专业的来干:硬件指令集(x64_86/armv8/嵌入式) → OS操作系统(仅提供API与浏览器,负责管理好硬件调度即可,不再支持原生开发,相当于import os
)→ 浏览器(python)+搜索引擎(pip) → 可缓存的APP/网站(.py)
杜绝那种一个平台自己写个搜索引擎来垄断的那种……
package: GetX
MVC: Model View Controller
一个Controller ←维护→ 多组数据Model
多个View ↔ 多个Controller,Controller给View数据,View将变化状态传给Controller
业务1:后端数据 转 前端模型
//api_client.dart
import 'dart:convert';
import 'package:get/get.dart';
class ApiClient extends GetConnect implements GetxService {
ApiClient();
static final _client = GetConnect();
static const String endpoint='http://127.0.0.1:8000/v1/';
Future<Map<String, dynamic>> sendGetRequest({
required String pathParams,
Map<String, dynamic>? queryParams,
Map? headers,
Map<String, dynamic>? body,
}) async {
final baseUrl = Uri.parse(endpoint+pathParams);
final queryParameters = queryParams?.cast<String, dynamic>() ?? {};
const contentType = 'application/json';
//* Headers da requisicao
final defaultHeaders = headers?.cast<String, String>() ?? {}
..addAll({
'accept': 'application/json',
// 'X-Parse-Application-Id': 'g1Oui3JqxnY4S1ykpQWHwEKGOe0dRYCPvPF4iykc',
// 'X-Parse-REST-API-Key': 'rFBKU8tk0m5ZlKES2CGieOaoYz6TgKxVMv8jRIsN',
});
if (queryParams != null) {
if (queryParams is int) {
queryParameters['param'] = queryParams;
} else {
queryParameters.addAll(queryParams);
}
}
final url = baseUrl.replace(queryParameters: queryParams);
try {
final response = await _client.get(
url.toString(),
decoder: (data) => json.decode(data as String),
headers: defaultHeaders,
contentType: contentType,
query: queryParameters,
);
return response.body as Map<String, dynamic>;
} catch (error) {
// * Retorno de Map vazio para erro generalizado
return {'error':error};
}
}
Future<Response> putUpd(String table, Map json) {
return put('http://$endpoint/$table', json);
}
Future<Response> postIns(String table, Map json) {
return post('http://$endpoint/$table', json);
}
Future<Response> dele(String table, Map<String, dynamic> json) {
return delete('http://$endpoint/$table', query: json);
}
@override
void onInit() {
super.onInit();
//修改请求
httpClient.addRequestModifier<void>((request) {
// request.headers['Authorization'] = '123';
return request;
});
//修改响应
httpClient.addResponseModifier((request, response) {
return response;
});
}
}
//controller.dart
import 'package:flut/common/api/api_client.dart';
import 'package:flut/common/models/models.dart';
import 'package:get/get.dart';
class UserinfoController extends GetxController {
late Rx<Userinfo> userinfo;
UserinfoController(this.queryParams);
var queryParams = <String, dynamic>{}.obs;
final ApiClient getUserinfo = Get.find();
instantiate(queryParams) async {
var response = await getUserinfo.sendGetRequest(
pathParams: 'userinfo', queryParams: queryParams);
userinfo = Userinfo.fromJson(response).obs;
return userinfo;
}
@override
void onInit() {
// TODO: implement onInit
super.onInit();
instantiate(queryParams);
}
}
models.dart
//models.dart
extension NullSaveParser on String {
DateTime? tryToDatetime() {
return DateTime.tryParse(this);
}
}
class Userinfo {
final String userinfo_id;
final String username;
final String pwd;
final String pwd_sault;
final String? nick;
final String? phone;
final String? email;
final String? numro;
final String? avatar;
final String? sex;
final DateTime? birth;
final String? pos;
final num days_online;
final DateTime last_online;
final num posts_watched;
final num posts_count_ord;
final num posts_count_works;
final String status;
final num following;
final num friends;
final num outcome_total;
final num outcome_monthly;
final num supporting;
Userinfo(
this.userinfo_id,
this.username,
this.pwd,
this.pwd_sault,
this.nick,
this.phone,
this.email,
this.numro,
this.avatar,
this.sex,
this.birth,
this.pos,
this.days_online,
this.last_online,
this.posts_watched,
this.posts_count_ord,
this.posts_count_works,
this.status,
this.following,
this.friends,
this.outcome_total,
this.outcome_monthly,
this.supporting);
Userinfo.fromJson(Map<String, dynamic> json)
: userinfo_id = json['userinfo_id'],
username = json['username'],
pwd = json['pwd'],
pwd_sault = json['pwd_sault'],
nick = json['nick'],
phone = json['phone'],
email = json['email'],
numro = json['numro'],
avatar = json['avatar'],
sex = json['sex'],
birth = json['birth'],
pos = json['pos'],
days_online = json['days_online'],
last_online = json['last_online'],
posts_watched = json['posts_watched'].toInt(),
posts_count_ord = json['posts_count_ord'].toInt(),
posts_count_works = json['posts_count_works'].toInt(),
status = json['status'],
following = json['following'].toInt(),
friends = json['friends'].toInt(),
outcome_total = json['outcome_total'],
outcome_monthly = json['outcome_monthly'],
supporting = json['supporting'].toInt();
Map<String, dynamic> toJson() => {
'userinfo_id': userinfo_id,
'username': username,
'pwd': pwd,
'pwd_sault': pwd_sault,
'nick': nick,
'phone': phone,
'email': email,
'numro': numro,
'avatar': avatar,
'sex': sex,
'birth': birth,
'pos': pos,
'days_online': days_online,
'last_online': last_online,
'posts_watched': posts_watched,
'posts_count_ord': posts_count_ord,
'posts_count_works': posts_count_works,
'status': status,
'following': following,
'friends': friends,
'outcome_total': outcome_total,
'outcome_monthly': outcome_monthly,
'supporting': supporting,
};
}
业务2:初始化10个类,渲染为ListView
Q1: 申请10组Post类数据,是创建10个PostController赋值给1个List,还是创建1个PostsController<List
>
A1:
Q2: 创建GetxController太多会影响性能吗?
A2: 多少都会
fastAPI
为什么会有路径参数和查询参数,为什么不直接用HTTP请求体包装前二者呢?
• 路径参数和查询参数是一种通过 URL 来传递参数的方式,它们可以让 API 的设计更加清晰和语义化,也可以方便地进行缓存和重定向
• 路径参数和查询参数通常用于 GET 请求,而请求体通常用于 POST、PUT、PATCH 等请求。GET 请求的目的是获取资源,而 POST 等请求的目的是创建或修改资源
• 路径参数和查询参数有一些限制,例如长度、字符集、编码等,而请求体则没有这些限制,可以传递更复杂和更大的数据
坑
return 报错:
File "D:\ProgramData\miniconda3\Lib\site-packages\fastapi\encoders.py", line 235, in jsonable_encoder
data = vars(obj)
^^^^^^^^^
TypeError: vars() argument must have dict attribute
解决方案:
Django
https://www.django-rest-framework.org/api-guide/schemas/
sqlAlchemy
直接使用这个,不用字符串处理,直接ORM数据库映射到python对象
Oracle
官方support需要购买企业账号,闭源数据库,很难找解决方案
坑1.与MySQL不同,不会自动commit
2. ORDS的Web Sql Developer 无法登录
原来写到CDB里,必须用PDB才能使用ORDS的RestApi功能
CDB默认为CDB$ROOT
,服务名XE
PDB默认为PDB$SEED
,服务名XEPDB1
localhost:8080/ords/hr/用户名(或者Schema名)
python连接oracle常用库
- cx_Oracle
是Python与Oracle数据库通信的最常用的库之一。它提供了与Oracle数据库进行连接、查询、事务处理等操作的功能。它是使用C编写的,因此在性能和稳定性方面表现出色。 - pyodbc
是一个通用的Python数据库接口库,可以用于连接多种类型的数据库,包括Oracle。它使用ODBC(开放数据库连接)作为中间层,允许与各种不同的数据库进行通信。 - sqlalchemy
是Python中一个非常强大的数据库工具库,它提供了一套统一的API,用于与多种数据库进行交互,包括Oracle数据库。它可以帮助简化数据库连接和查询操作,并提供ORM(对象关系映射)功能。 - pyoracle
是一个基于cx_Oracle的Python库,提供了一些额外的功能和抽象,使连接和操作Oracle数据库更容易。
————————————————
版权声明:本文为CSDN博主「SunnyRivers」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Android_xue/article/details/130740414
如何快速阅读开发文档
生动例子
原来版本号不一样,导致步骤无法进行……
https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/20.2/aelig/using-multitenant-architecture-oracle-rest-data-services.html
https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/22.1/ordig/about-REST-configuration-files.html
Tauri
很有希望的跨平台框架,把flutter对web的支持不好的坑填了,最小打包可以比Flutter更小,但UI渲染的内存比Flutter会大
但Web的话,管你内存,一定是Tauri下的web比flutter更流畅
内存占用:
Tauri+RUST: 8.5MB
flutter: windows会带一个18MB的flutter_windows.dll
flutter的优势:
- 嵌入式开发(内存敏感),毕竟不是每个树莓派都会自带webview实现
- dart vs typescript
- WASM无缝支持(但估计仍对SEO不友好)
更详细的 Tarui vs Flutter:https://zhuanlan.zhihu.com/p/520770477