返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
项目开发小结系列

Project Completion Summary: WeChat Mini Program developed using DjangoStarter v3 and Taro

Backend DjangoStarter v3 flash sale development, ninja API is awesome to write. Frontend Taro for WeChat Mini Program? Ha, WeChat is a mess but somehow has a moat? django-filer and tailwindcss are decent, sharing done, but I've been procrastinating on the code for too long.

曦远 (DealiAxy)约 30 分钟
目录14 个章节

Preface

Without realizing it, September has already arrived, marking another back-to-school season. The project wall I've wanted to create every year still hasn't even materialized... 😂

Lately, there have been too many trivial matters in life, leaving me with no motivation to write articles at all. However, no matter how much I procrastinate, I still need to document things. So, I'll just write casually~

This was a small project from July, not particularly technical in nature—it was more of a friendly development effort. The backend uses DjangoStarter v3, and the frontend uses Taro for the WeChat Mini Program.

To be honest, there's not much to say about the backend. Any pitfalls worth noting are mostly on the Mini Program side. I have to say WeChat is absolutely terrible—not only is the code a mess, but the documentation is even worse 😂

I recall seeing a perspective online that it's precisely because WeChat is so awful that it creates a moat for Mini Program developers. Now, thinking about it, that actually makes a lot of sense. Hehe 😃

Backend Section

Let's start with the backend part.

Ever since DjangoStarter, developing the backend for such small projects has become a breeze.

This project uses a few new tools, which I've already introduced in previous articles. I'll just share the links here:

Using django-filer for File Management

I forgot that I haven't written about this yet, so here's a brief introduction. This component allows for centralized management of files (including images) and provides an admin interface. However, the UI style is quite basic, and there's a bug with the Chinese locale—it needs manual adjustment to switch to Chinese properly. Still, it's sufficient for this project.

Project URL: https://github.com/django-cms/django-filer

Installation: pdm add django-filer

Add filer to INSTALLED_APPS.

Create a configuration file at src/config/settings/components/filer.py.

THUMBNAIL_PROCESSORS = (
    'easy_thumbnails.processors.colorspace',
    'easy_thumbnails.processors.autocrop',
    'easy_thumbnails.processors.scale_and_crop',
    # Subject location aware cropping
    # 'filer.thumbnail_processors.scale_and_crop_with_subject_location',
    'easy_thumbnails.processors.filters',
)

FILER_ENABLE_LOGGING = True

Finally, modify the fields that require attachments.

from filer.fields.image import FilerImageField

class Brand(ModelExt):
    name = models.CharField('Name', max_length=100)
    # logo = models.ImageField(upload_to='brand_logos/')
    logo = FilerImageField(
        verbose_name='Logo', null=True, blank=True,
        related_name='+', on_delete=models.SET_NULL,
    )

That's about it. For specific usage, please refer to the official documentation~

Using Ninja to Write APIs

DjangoStarter v3 switched from drf to ninja.

This ninja is very similar to FastAPI, and it's much more comfortable to write than drf. Although it can't automatically generate crud interfaces like drf, it doesn't require much more code and is actually more flexible.

(P.S.: DjangoStarter v3 still supports automatic crud interface generation. Additionally, third-party libraries can be used to automatically generate crud interfaces based on ninja. However, I haven't used them yet. You can refer to the extension section below.)

For example, the django-filer library mentioned earlier: the image field is actually a foreign key pointing to the filer.fields.models.File model. Therefore, if the interface doesn't handle it, the generated data will just be a foreign key ID. That's why the schema needs to be modified.

class CaseOut(ModelSchema):
  cover_image: str

  @staticmethod
  def resolve_cover_image(obj: Case):
    if not obj.cover_image:
      return f'https://starblog/Api/PicLib/Random/{obj.id}/250/150'
    return obj.cover_image.url

  class Meta:
    model = Case
    fields = [
      'id', 'is_deleted', 'created_time', 'updated_time', 'name', 'description',
      'category', 'car_model', 'part', 'build_time',
    ]

The code above will render the cover_image field as an image address. If cover_image doesn't exist, it returns a random image address.

The schema separates logic from data rendering, resulting in a clearer code structure.

More complex logic can also be implemented simply by defining a resolve_field_name method.

Ninja Ecosystem Expansion

I also just discovered recently that django-ninja actually has a Chinese website, which lists several extensions in the Ninja ecosystem. They all seem quite promising, and I might give them a try when I have time.

Website: https://django-ninja.cn/

  • Ninja JWT - A JSON Web Token authentication plugin for the Django Ninja REST framework.
  • Django Ninja Extra - Django Ninja Extra offers a class-based approach along with additional features, which will accelerate your RESTful API development using Django Ninja.
  • Django Ninja CRUD - Django Ninja CRUD is a robust, declarative, and somewhat opinionated framework that simplifies the process of developing CRUD (Create, Read, Update, Delete) endpoints with Django Ninja. It also provides a declarative, scenario-based approach for testing these endpoints using Django REST Testing (a companion package).

Mini-program section

This time, I still used Taro to develop the mobile end. Last time it was for H5 (official accounts), and this time I tried how the mini-program made with Taro would turn out. The actual effect is still acceptable.

For state management, I still chose MobX. I'm used to it. There are too many front-end wheels, and I'm too lazy to try others~

Compared to the backend part, there's a bit more to write for the mini-program (but not much, and it's all quite simple).

Using tailwindcss in Taro

Recently, I started using tailwindcss and immediately loved this highly efficient styling tool (although it can lead to very long strings of classes). However, its flaws are minor compared to its benefits—using tailwindcss makes it incredibly easy to copy various styles online and even lets LLMs help me create styles, maximizing productivity~

Taro officially provides support for tailwindcss, which is excellent. All you need to do is follow the official documentation.

For details, see the official documentation: https://docs.taro.zone/docs/tailwindcss

Sharing Mini-Programs

If not actively invoked, only by registering the onShareAppMessage event will the forwarding button be displayed when clicking the three dots in the upper right corner. Similarly, only by registering the onShareTimeline event can you share to the Moments (朋友圈).

Correspondingly, Taro provides hooks (钩子) for these two events. Let's look at the code directly.

import Taro, {useShareAppMessage, useShareTimeline} from '@tarojs/taro'

const CasePage = () => {   
  const getCase = async () => {
    if (!id) return
    const data = await CaseService.get(Number(id))
    console.log('get case', data)
    setCaseData(data.data)
    return {
      title: `案例:${data.data.name}`,
      path: RouterMap.car.case(data.data.id),
      imageUrl: data.data.cover_image
    }
  }

  useShareAppMessage(res => {
    console.log('执行分享操作', res)
    return {
      title: '案例分享',
      path: RouterMap.index,
      promise: new Promise(resolve => getCase().then((data) => {
        // @ts-ignore
        resolve(data)
      })),
    }
  })

  useShareTimeline(() => {
    console.log('执行分享朋友圈操作')

    return {
      title: `案例:${caseData.name}`,
      query: `id=${caseData.id}`,
      imageUrl: caseData.cover_image
    }
  })
}

It's the same as described in the mini-program documentation, and the parameters to be returned are also identical, so just refer to the documentation directly~

Active Sharing

For active sharing, you just need to set the button to the share type. Taro has also wrapped this.

<Button type='info' icon={<Share/>} openType='share'>Share</Button>

References

User Login

In this project, I implemented a separate page to handle login. It could also be done using a Modal. Initially, I was hopeful about retrieving user information, since the documentation mentions that accessing user avatars, nicknames, etc., requires explicit user action. That's why I placed it on a standalone page with a login button for users to click.

However, I later discovered that newer versions of the mini-program no longer support retrieving information this way...

Taro has already encapsulated the login process. Essentially, it involves replacing the wx in wx.login with Taro.

const LoginPage = observer(() => {
  const handleLogin = async (data: LoginToken) => {
    UserStore.login({
      token: data.token,
      exp: String(data.exp),
    })

    const userInfo = await AccountService.getCurrentUser()
    console.log('获取用户信息', userInfo)
    UserStore.userInfo = userInfo.data
    Taro.navigateBack()
  }

  const autoLogin = async () => {
    Taro.showToast({title: '正在登录', icon: 'loading'})
    Taro.login({
      success: async (res) => {
        if (res.code) {
          console.log('小程序登录,获取code', res.code)
          const resp = await AccountService.loginWeApp(res.code)
          console.log('小程序登录,请求后端', resp)
          await handleLogin(resp.data)
        } else {
          console.log('登录失败!', res.errMsg)
        }
      }
    })
  }
}

After obtaining the code from the mini-program's OAuth, I called the backend interface to log in. That's all there is to it.

Furthermore, DjangoStarter v3 has integrated this part as well 🕶

Click image to open full-size view

Just call Taro.previewImage within the image component's onTap event. Remember to pass in the URL of the large image (if the thumbnail uses a separate address).

<Image
  key={e.id} src={e.url}
  onTap={() => {
    Taro.previewImage({
      current: e.url,
      urls: caseData.images.map(i => i.url)
    })
  }}
  className="w-full rounded shadow"
  mode="aspectFill"
/>

Deployment

This deployment is also a brand new version.

This project is also the first practice of the new version of DjangoStarter, so I've written blog posts documenting some of the pitfalls I encountered.

Overall, the daphne server (daphne 服务器) works quite well.

Summary

As mentioned at the beginning, this project was relatively simple. Most of the time was spent on frontend interactions and adjusting some details.

Using Taro for mini-program development was smooth. Potential issues with packaging and deployment that I anticipated actually didn’t arise.

This summary has really been delayed for too long, and I haven’t written code in a while either… I need to organize my thoughts and start working on everything I’ve wanted to do.

_EOF_

Project Completion Summary: WeChat Mini Program developed using DjangoStarter v3 and Taro

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

项目完成小结-民政局项目-Django3.x版本-开发部署小结(2)

前言 好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结 这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家都身心疲惫。唉,只能说技术团队,有里一个靠谱有能力的领导是非常重要的。 进入正题 本文继续记录Django项目开发的一些经验。 本次的项目依然基于我定制的「DjangoStarter」项目模板来开发,

2022-05-17 · 项目开发小结系列 · 约 1 分钟

项目完成小结:使用Blazor和gRPC开发大模型客户端

其实几年前我就有轻度使用了一下 Blazor 这个技术,一开始使用 Blazor ,我是有点嫌弃的,我还是比较倾向于传统的前后端分离,AspNetCore用来做后端,用 React 做前端,生态很丰富,要做啥组件都容易。这次只是个小 Demo 项目,试用了一下 Blazor ,从一开始的非常别扭,到越来越顺手。感觉 Blazor Server 写小项目还是挺好用的,后面继续完善项目,持续发掘 B

2023-07-14 · 项目开发小结系列 · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…

Project Completion Summary: WeChat Mini Program developed using DjangoStarter v3 and Taro · StarBlog