全栈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常用库

  1. cx_Oracle
    是Python与Oracle数据库通信的最常用的库之一。它提供了与Oracle数据库进行连接、查询、事务处理等操作的功能。它是使用C编写的,因此在性能和稳定性方面表现出色。
  2. pyodbc
    是一个通用的Python数据库接口库,可以用于连接多种类型的数据库,包括Oracle。它使用ODBC(开放数据库连接)作为中间层,允许与各种不同的数据库进行通信。
  3. sqlalchemy
    是Python中一个非常强大的数据库工具库,它提供了一套统一的API,用于与多种数据库进行交互,包括Oracle数据库。它可以帮助简化数据库连接和查询操作,并提供ORM(对象关系映射)功能。
  4. 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的优势:

  1. 嵌入式开发(内存敏感),毕竟不是每个树莓派都会自带webview实现
  2. dart vs typescript
  3. WASM无缝支持(但估计仍对SEO不友好)
    更详细的 Tarui vs Flutter:https://zhuanlan.zhihu.com/p/520770477
posted @ 2023-09-22 14:36  Nolca  阅读(158)  评论(0编辑  收藏  举报