Django:如何使用django.forms.ModelChoiceField和原始SQL查询?

4

我试图使用一个组合框展示相关的实体表单,因此我使用了一个ModelChoiceField。

这种方法很有效,直到我需要限制显示哪些实体时。如果我使用一个简单的查询表达式,它也可以正常工作,但是如果我使用一个原始的SQL查询,就会出现问题。

因此,我的代码设置了一个查询过滤表达式来使其正常工作。

class ReservationForm(forms.Form):
    location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")

def __init__(self,*args,**kwargs):
    city_id = kwargs.pop("city_id")     # client is the parameter passed from views.py
    super(ReservationForm, self).__init__(*args,**kwargs)

    # TODO: move this to a manager        
    self.fields['location_time_slot'].queryset = LocationTimeSlot.objects.filter(city__id = city_id )

但是,如果我将其更改为原始查询,则会出现问题。不起作用的代码:

class ReservationForm(forms.Form):
        location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")

    def __init__(self,*args,**kwargs):
        city_id = kwargs.pop("city_id")     # client is the parameter passed from views.py
        super(ReservationForm, self).__init__(*args,**kwargs)

        # TODO: move this to a manager        
        query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order' 
    FROM  reservations_locationtimeslot AS ts
     INNER JOIN reservations_location AS l ON l.id = ts.location_id 
     WHERE l.city_id = %s     
     AND ts.available_reserves > 0  
     AND ts.datetime_from > datetime() """

    time_slots = LocationTimeSlot.objects.raw(query, [city_id])

    self.fields['location_time_slot'].queryset = time_slots

尝试渲染小部件时,我遇到的第一个错误是:“'RawQuerySet'对象没有属性'all'”。
感谢此处的一位评论者,我解决了这个问题,方法如下:
 time_slots.all = time_slots.__iter__ # Dummy fix to allow default form rendering with raw SQL

现在当我提交表单时,出现了类似的问题:
'RawQuerySet'对象没有属性'get'

有没有合适的方法来准备RawQuerySet以便被ModelChoiceField使用?

谢谢!

2个回答

1
我通过添加缺失的方法暂时解决了问题。目前我使用ModelChoiceField的方式只需要添加all()和get()方法,但在不同的情况下你可能还需要添加其他方法。此外,这不是一个完美的解决方案,因为: 1)以这种方式定义get方法可能会产生不正确的结果。我认为get()方法用于验证所选选项是否在all()返回的选项中。我临时实现的方式仅验证id是否存在于表中。 2)我猜测以这种方式指定get方法的性能较低。
如果有人能想到更好的解决方案,请告诉我。
所以我的临时解决方案:
class LocationTimeSlotManager(models.Manager):
    def availableSlots(self, city_id):
        query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order' 
            FROM  reservations_locationtimeslot AS ts
            ..... 
            ..... 
            MORE SQL """ 

        time_slots = LocationTimeSlot.objects.raw(query, [city_id])

        # Dummy fix to allow default form rendering with raw SQL
        time_slots.all = time_slots.__iter__ 
        time_slots.get = LocationTimeSlot.objects.get

        return time_slots

1

您确定您实际上需要一个原始查询吗?仅查看该查询,我看不出任何理由您不能使用filter(location__city=city_id, available_reserves__gte=0, datetime_from__gt=datetime.datetime.now())来完成它。

原始查询集缺少许多在常规查询集上定义的方法,因此仅将它们放置在原处可能无法正常工作,除非编写所有这些方法的自定义定义。


1
是的,我发布了一个过于简化的查询。生产环境中的查询要复杂得多,并且预计会增长 :-(因此,总之,您的意思是Symfony的ModelChoiceField没有准备好使用RawQuerySets? - MAB
2
ModelChoiceField 没有设置使用原始查询集的方法,除非您填写必要的方法。根据您的情况,声明数据库中的视图并创建一个未管理的 Django 模型来表示它可能是有用的。您需要额外的工作将所选视图条目转换回真实的模型实例,但这不应该太难,因为您可以保持 PK 不变。 - Peter DeGlopper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接