【问题标题】:Django Rest Framework: SerializerMethodField and get_querysetDjango Rest 框架:SerializerMethodField 和 get_queryset
【发布时间】:2018-11-21 13:49:06
【问题描述】:

在我的数据库中,我有两个表 - usersuser_properties

我需要使用 GET 参数phone 过滤输出。但是这两个表都有 phone 列并且具有不同的值。

当我执行 GET 请求(例如“?phone=123456789”)时,我不仅需要使用 user_properties.phone,还需要使用 user.phone,通过电话号码搜索用户!

我在谷歌上搜索并找到了一种使用 get_queryset(filtering) 和 SerializerMethodField(修改输出)的方法:

models.py

class User(models.Model):
    balance = models.DecimalField(decimal_places=35, max_digits=40)
    fio = models.CharField(max_length=64)
    phone = models.CharField(max_length=64, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'user'


class UserProperties(models.Model):
    user_id = models.IntegerField(primary_key=True)
    phone = models.CharField(max_length=11)

    class Meta:
        managed = False
        db_table = 'user_properties'

views.py

class UserPropertiesViewSet(viewsets.ModelViewSet):
queryset = UserProperties.objects.all()
serializer_class = serializers.UserPropertiesSerializer

def get_queryset(self):
    queryset = self.queryset
    # phone number from GET
    phone = self.request.query_params.get('phone')

    # Search users matches in user_properties using by phone number
    if phone:
        queryset = UserProperties.objects.all()
        users = queryset.filter(phone__contains=phone)
        return users
    else:
        return queryset

serializers.py

class UserPropertiesSerializer(serializers.ModelSerializer):
all_phones = serializers.SerializerMethodField()

class Meta:
    model = models.UserProperties
    fields = ['user_id', 'phone', 'fio', 'url', 'all_phones',]

# phone numbers from user and user_properties tables
def get_all_phones(self, obj):
    # search phones in <user> table by user_id
    user_phones = models.User.objects.filter(id__exact=obj.user_id).values_list('phone', flat=True)
    # add phones from user_properties table
    result = [obj.phone,]
    # add phones from user table
    for phone in user_phones[0].split(','):
        result.append(''.join(filter(lambda x: x.isdigit(), phone)))

    # return list with all phones
    return set(result)

我在过滤结果中得到all_phones 列:

{
"count": 1,
"next": null,
"previous": null,
"results": [
    {
        "user_id": 17897,
        "phone": "123456789", <--- user_properties table
        "fio": "....",
        "url": "....",
        "all_phones": [
            "123456789",
            "5512222",
            "49543"
        ] <--- user_properties.phone + user.phone
    }
]

}

但在get_queryset 中,我仅使用 user_properties 表中的“真实”phone 列进行过滤。如何使用“虚拟”all_phones 列过滤结果?或者在两个表中搜索并获取拥有此电话号码之一的用户?有可能吗?还是我的方法不对?

谢谢!

【问题讨论】:

  • 您不能依赖 Django ORM 来搜索 2 个不同的表。您要么必须查询两次并加入结果,要么标准化您的数据并将所有电话号码移动到一个表中。

标签: django rest api django-rest-framework


【解决方案1】:

这个问题更多是关于 Django ORM 而不是 Django REST 框架,但是像这样重写 get_queryset 方法将搜索两个模型的 phone 字段:

from django.db.models import Q

...

def get_queryset(self):
    queryset = self.queryset
    # phone number from GET
    phone = self.request.query_params.get('phone')

    if phone:
        queryset = queryset.filter(
            Q(phone__contains=phone)
            | Q(user__phone__contains=phone)
        )

    return queryset

【讨论】:

  • 嗨。谢谢您的回答。我收到一个错误:“”无法将关键字“用户”解析到字段中。选项有:电子邮件、电话、“...等(来自 user_properties 表的列列表)。Q 是否可以处理两个没有关系的表?似乎没有
  • 为了使示例工作,表需要定义一个关系 - ForeignKeyOneToOneField。您能否更新您的帖子,包括您的 User 和 UserProperties 模型的相关部分?
  • 更新了我的帖子。我无法更改我的数据库表 - 只读。
  • 没关系,修改模型定义应该就够了。更改user_id 字段定义并重试:user = models.OneToOneField(User, primary_key=True)(注意该字段已从user_id 重命名为user
  • 谢谢!!!这就是我需要的。但是你能解释一下:我以后运行“迁移”命令后,Django 不会更改表 user_properties 吗?我已经 managed=False 并且当我运行 migrate 命令时,我添加了 --fake 关键字。
猜你喜欢
  • 2015-07-14
  • 1970-01-01
  • 1970-01-01
  • 2017-12-19
  • 2020-12-20
  • 2020-10-11
  • 2021-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多