12.寻光集后台管理系统-库存信息(后端)

2022-12-02 16:09:54 浏览数 (1)

有了产品信息之后,下面来写库存信息部分

每一个库存的入库和出库都需要对应一个产品,所以产品信息是它的外键

库存信息

创建App

代码语言:javascript复制
python manage.py startapp warehouse

warehouse文件夹移动到backend/apps/warehouse

注册App

backend/LightSeeking/settings.pyINSTALLED_APPS中添加

代码语言:javascript复制
INSTALLED_APPS = [
    ...
    'users.apps.UsersConfig',
    'product.apps.ProductConfig',
    'warehouse.apps.WarehouseConfig'
]

表结构设计

库存信息包含了

  • 产品信息
  • 单价
  • 生产日期
  • 保质期天数
  • 数量
  • 库存状态
  • 订单号
  • 供应商
  • 备注

产品信息使用外键的方式,并且两个键之间不互相影响

代码语言:javascript复制
product = models.ForeignKey(Product, verbose_name="产品信息", on_delete=models.DO_NOTHING, help_text="产品信息")

其他都是普通的字段

代码语言:javascript复制
from django.db import models

from product.models import Product
from utils.models import BaseModel


class Warehouse(BaseModel):
    STOCKSTATUS = (
        (, "入库"),
        (, "出库"),
        (, "报废"),
    )
    product = models.ForeignKey(Product, verbose_name="产品信息", on_delete=models.DO_NOTHING, help_text="产品信息")
    price = models.DecimalField('成本单价', max_digits=, decimal_places=, default=, help_text='成本单价')
    date_of_manufacture = models.DateTimeField("生产日期", help_text="生产日期")
    quality_guarantee = models.IntegerField("保质期天数", help_text="保质期天数")
    num = models.IntegerField("数量", default=, help_text="数量")
    stock_status = models.CharField("库存状态", max_length=, choices=STOCKSTATUS, default=,
                                    help_text='库存状态:1-入库,2-出库,3-报废')
    order_id = models.CharField("订单号", max_length=, null=True, blank=True, default='', help_text='订单号')
    supplier = models.CharField("供应商", max_length=, null=True, blank=True, default='', help_text='供应商')
    desc = models.CharField('备注', max_length=, null=True, blank=True, default='', help_text='备注')

    class Meta:
        db_table = 'tb_warehouse'
        verbose_name = '库存信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.id

数据迁移

代码语言:javascript复制
python manage.py makemigrations
python manage.py migrate

序列化器

在库存序列化器中需要指定产品来进行新增,查看的时候需要返回全部的产品信息

所以先新建一个包含全部产品的序列化器

backend/apps/product/serializers.py

代码语言:javascript复制
class ProductAllModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

添加产品的时候使用的是产品的id,这时候就需要判断一下这个产品是否存在了

编写whether_existed_product_id方法来进行判断

代码语言:javascript复制
def whether_existed_product_id(value):
    """
    检查产品id是否存在
    :param value:
    :return:
    """
    if value != :
        if not isinstance(value, int):
            raise serializers.ValidationError('所选产品信息有误!')
        elif not Product.objects.filter(is_delete=False, id=value).exists():
            raise serializers.ValidationError('所选产品信息不存在!')

所以库存的序列化器基础版本为

代码语言:javascript复制
class WarehouseModelSerializer(serializers.ModelSerializer):
    product_id = serializers.IntegerField(
        write_only=True,
        help_text='产品信息ID',
        validators=[validates.whether_existed_product_id]
    )
    product = ProductAllModelSerializer(read_only=True, many=False)

    class Meta:
        model = Warehouse
        exclude = ('is_delete',)
        extra_kwargs = {
            'c_time': {
                'read_only': True
            },
        }
重点

下面是序列化器中的一个神奇的操作,也就是它可以逐行处理返回的数据,根据逻辑给每行添加需要的数据

在这个序列化器中需要顺便计算一下产品的保质期相关数据

新加两个字段剩余天数保质期日期

剩余天数 = 保质期天数 - (当前日期 - 生产日期)

保质期日期 = 生产日期 剩余天数

代码语言:javascript复制
remaining = serializers.SerializerMethodField(label="剩余天数", read_only=True, help_text="剩余天数")
remaining_day = serializers.SerializerMethodField(label="保质期日期", read_only=True, help_text="保质期日期")

使用serializers.SerializerMethodField可以在WarehouseModelSerializer类中编写get_xxx方法来计算这个值

代码语言:javascript复制
def get_remaining(self, warehouse):
    """
    剩余天数 = 保质期天数 - (当前日期 - 生产日期)
    :param warehouse:
    :return:
    """
    return warehouse.quality_guarantee - (
            warehouse.date_of_manufacture - datetime.datetime.now(tz=pytz.UTC)
    ).days

def get_remaining_day(self, warehouse):
    """
    保质期日期 = 生产日期 剩余天数
    """
    return warehouse.date_of_manufacture   datetime.timedelta(days=warehouse.quality_guarantee)

同理再根据需求添加几个字段

  • 总库存
  • 出库
  • 入库
  • 已经出库时间
  • 已经入库时间
代码语言:javascript复制
total = serializers.SerializerMethodField(label="总库存", read_only=True, help_text="一个产品的总库存")
warehouse1 = serializers.SerializerMethodField(label="出库", read_only=True, help_text="一个产品的总出库")
warehouse2 = serializers.SerializerMethodField(label="入库", read_only=True, help_text="一个产品的总入库")
warehouse1_time = serializers.SerializerMethodField(label="已经出库时间", read_only=True, help_text="出库距离当前的时间")
warehouse2_time = serializers.SerializerMethodField(label="已经入库时间", read_only=True, help_text="入库距离当前的时间")

实现字段的计算

代码语言:javascript复制
def get_total(self, warehouse):
    total = self.get_warehouse1(warehouse) - self.get_warehouse2(warehouse)
    return total

def get_warehouse1(self, warehouse):
    product_id = warehouse.product_id
    warehouse_info = Warehouse.objects.filter(stock_status=).values("product").annotate(total=Sum("num")).all()
    total = warehouse_info.filter(product_id=product_id) and 
            warehouse_info.filter(product_id=product_id).get()["total"] or 
    return total

def get_warehouse2(self, warehouse):
    product_id = warehouse.product_id
    warehouse_info = Warehouse.objects.filter(stock_status=).values("product").annotate(total=Sum("num")).all()
    total = warehouse_info.filter(product_id=product_id) and 
            warehouse_info.filter(product_id=product_id).get()["total"] or 
    return total

def get_warehouse1_time(self, warehouse):
    warehouse2_time = warehouse.c_time - datetime.datetime.now(datetime.timezone(datetime.timedelta(hours= )))
    return warehouse2_time.days

def get_warehouse2_time(self, warehouse):
    warehouse2_time = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours= ))) - warehouse.c_time
    return warehouse2_time.days

视图

代码语言:javascript复制
from rest_framework.viewsets import ModelViewSet

from utils.pagination import TenItemPerPagePagination
from warehouse.models import Warehouse
from warehouse.serializers import WarehouseModelSerializer


class WarehouseViewSet(ModelViewSet):
    queryset = Warehouse.objects.filter(is_delete=False).order_by("-c_time")
    serializer_class = WarehouseModelSerializer
    pagination_class = TenItemPerPagePagination
    ordering_fields = ['c_time']

    def perform_destroy(self, instance):
        instance.is_delete = True
        instance.save()  # 逻辑删除

路由

backend/apps/warehouse/urls.py

代码语言:javascript复制
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register("warehouse", views.WarehouseViewSet)

urlpatterns = [
    path('', include(router.urls))
]

backend/LightSeeking/urls.py

代码语言:javascript复制
urlpatterns = [
    ...
    path('', include('warehouse.urls')),
]

代码

本次代码修改见:

https://gitee.com/zx660644/light-seeking/commit/2a5f9e4a26ff56e7cef5e6d4bdaf0adc63bb37b0

测试

先添加一个产品信息

访问http://127.0.0.1:8000/product/

填入产品信息后点击POST

再访问http://127.0.0.1:8000/warehouse/

添加一个库存信息

查看结果

代码语言:javascript复制
{
    "id": ,
    "product": {
        "id": ,
        "c_time": "2022-09-02T12:14:32.356282 08:00",
        "u_time": "2022-09-02T12:14:32.356417 08:00",
        "is_delete": false,
        "product_id": "0001",
        "category": "饮料",
        "brand": "可口可乐",
        "name": "可乐",
        "price": "3.00",
        "sample_png": "",
        "desc": "无"
    },
    "remaining": ,
    "remaining_day": "2023-09-01T12:15:00 08:00",
    "total": ,
    "warehouse1": ,
    "warehouse2": ,
    "warehouse1_time": -1,
    "warehouse2_time": ,
    "c_time": "2022-09-02T12:15:46.366239 08:00",
    "u_time": "2022-09-02T12:15:46.366352 08:00",
    "price": "2.50",
    "date_of_manufacture": "2022-09-01T12:15:00 08:00",
    "quality_guarantee": ,
    "num": ,
    "stock_status": ,
    "order_id": "",
    "supplier": "测试供应商",
    "desc": ""
}

其中product字段包含了我们入库的产品信息

另外之前序列化器中添加的几个字段也出现了

bug修复

不过从这里看出来,之前写了个bug,之前把日期计算写反了

代码语言:javascript复制
def get_remaining(self, warehouse):
    """
    剩余天数 = 保质期天数 - (当前日期 - 生产日期)
    :param warehouse:
    :return:
    """
    return warehouse.quality_guarantee - (
            datetime.datetime.now(tz=pytz.UTC) - warehouse.date_of_manufacture
    ).days

再次刷新后查看 remaining变为了364

代码语言:javascript复制
{
    "data": {
        "page": ,
        "pageSize": ,
        "rows": [
            {
                "id": ,
                "product": {
                    "id": ,
                    "c_time": "2022-09-02T12:14:32.356282 08:00",
                    "u_time": "2022-09-02T12:14:32.356417 08:00",
                    "is_delete": false,
                    "product_id": "0001",
                    "category": "饮料",
                    "brand": "可口可乐",
                    "name": "可乐",
                    "price": "3.00",
                    "sample_png": "",
                    "desc": "无"
                },
                "remaining": ,
                "remaining_day": "2023-09-01T04:15:00Z",
                "total": ,
                "warehouse1": ,
                "warehouse2": ,
                "warehouse1_time": -1,
                "warehouse2_time": ,
                "c_time": "2022-09-02T12:15:46.366239 08:00",
                "u_time": "2022-09-02T12:15:46.366352 08:00",
                "price": "2.50",
                "date_of_manufacture": "2022-09-01T12:15:00 08:00",
                "quality_guarantee": ,
                "num": ,
                "stock_status": ,
                "order_id": "",
                "supplier": "测试供应商",
                "desc": ""
            }
        ],
        "total": 
    },
    "message": "",
    "code": ,
    "next": null,
    "previous": null
}

0 人点赞