Django无ManyToMany关系时跨模型查询关联数据及报错解决
问题背景
现有Django项目中WatchList模型通过外键listing关联Listings模型,需要查询指定用户WatchList下的所有Listing数据在页面展示,不希望改用ManyToManyField避免影响已调试完成的相关功能模块。现有代码运行抛出TypeError: cannot unpack non-iterable function object错误,错误由watchlist_listing = watchlists.get(listing)行触发。
现有代码
models.py
class Listings(models.Model): CATEGORY = [ ("Miscellaneous", "Miscellaneous"), ("Movies and Television", "Movies and Television"), ("Sports", "Sports"), ("Arts and Crafts", "Arts and Crafts"), ("Clothing", "Clothing"), ("Books", "Books"), ] title = models.CharField(max_length=64) description = models.CharField(max_length=500) bid = models.DecimalField(max_digits=1000000000000, decimal_places=2) image = models.URLField(null=True, blank=True) category = models.CharField(max_length=64, choices=CATEGORY, default=None) user = models.ForeignKey(User, on_delete=models.CASCADE, default="") class WatchList(models.Model): listing = models.ForeignKey(Listings, on_delete=models.CASCADE, default="") user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
错误版本views.py
def watchlist(request): watchlists = WatchList.objects.filter(user=request.user) for listing in watchlists.listing: listings_needed = watchlists.listing() watchlist_listing = watchlists.get(listing) listings = Listings.objects.all().filter(watchlist_listing) return render(request, "auctions/watchlist.html",{ "listings": listings })
错误原因
WatchList.objects.filter(user=request.user)返回的是包含多条WatchList记录的QuerySet集合,不是单个模型实例,不存在直接的.listing属性,for listing in watchlists.listing的循环写法本身不成立。get()方法要求传入键值对格式的查询条件(例如listing=某实例),直接传入变量listing会被Django识别为查询参数名,无法解析为合法的过滤表达式,因此抛出无法解包的错误。- 最后
Listings.objects.all().filter(watchlist_listing)的过滤逻辑不合法,没有指定过滤字段和匹配值,无法执行查询。
实现方案
不需要修改现有模型结构,不需要替换为多对多字段,两种写法都可以满足需求:
方案1:跨表反向查询(推荐,代码最简洁)
利用Django ORM的跨表查询语法,直接过滤Listings表即可:
def watchlist(request): # 双下划线__表示关联到WatchList表,匹配WatchList中user为当前用户的记录对应的Listing listings = Listings.objects.filter(watchlist__user=request.user) return render(request, "auctions/watchlist.html", { "listings": listings })
方案2:先查WatchList再提取关联Listing
如果后续需要用到WatchList自身的字段(比如新增的收藏时间字段),可以先查询用户的所有WatchList记录,再批量提取关联的Listing对象:
def watchlist(request): # select_related做连表优化,避免循环取关联对象时产生重复数据库查询 watchlists = WatchList.objects.filter(user=request.user).select_related("listing") listings = [item.listing for item in watchlists] return render(request, "auctions/watchlist.html", { "listings": listings })
内容的提问来源于stack exchange,提问作者skateb2020
相关产品推荐
相关产品推荐

