Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

介紹一些 Model 與 Manager 的使用策略 #24

Open
uranusjr opened this issue Jul 8, 2015 · 4 comments
Open

介紹一些 Model 與 Manager 的使用策略 #24

uranusjr opened this issue Jul 8, 2015 · 4 comments

Comments

@uranusjr
Copy link
Owner

uranusjr commented Jul 8, 2015

Fat model and mangers, forms when suitable, thin views, trivial templates.

節錄一些 Gitter 對話


壞範例:

from django.db import models

class Restaurant(models.Model):
    is_fastfood = models.BooleanField()
    average_price = models.IntegerField()

# In view.
some_restaurants = Restaurant.objects.filter(
    is_fastfood=False, average_price__gte=100,
)

好範例:

from django.db import models

class RestaurantQuerySet(models.QuerySet):
    def some(self):
        return self.filter(is_fastfood=False, average_price__gte=100)


class Restaurant(models.Model):
    is_fastfood = models.BooleanField()
    average_price = models.IntegerField()
    objects = RestaurantQuerySet.as_manager()

# In view.
some_restaurants = Restaurant.objects.some()

最主要的好處其實是會讓你的 view 很好讀

找到一個好像還不錯的例子

有個 project 需要找到使用者在一個範圍內能看到的所有 remote 物件

# 一個頁面顯示一個 region
user = request.user
if not user.is_authenticated():
    remotes = Remotes.objects.none()
else:
    remotes = Remote.objects.filter(
        location__isnull=False,
        location__contained=region.boundary,
    )
    if not user.is_superuser:
        remotes = remotes.filter(owner_set__in=[user])

邏輯大概像這樣

我們把測試使用者權限的程式抽出來

class OwnableQuerySet(QuerySet):
    def get_viewable(self, user):
        # Exclude objects not owned by user unless the user is a superuser.
        if not user.is_authenticated():
            return self.none()
        elif user.is_superuser:
            return self.all()
        return self.filter(owner_set__in=[user])

然後把找 region 中 remotes 的程式抽出來

class Region(models.Model):
    # ...
    def get_visible_remotes(self, user):
        remotes = Remote.objects.filter(
            location__isnull=False,
            location__contained=self.boundary,
        )
        remotes = remotes.get_viewable(user=user)
        return remotes

最後在 view 裡就變成這樣:

# 一個頁面顯示一個 region
remotes = region.get_visible_remotes(user=request.user)
@uranusjr
Copy link
Owner Author

uranusjr commented Jul 8, 2015

突然發現 get_visible_remotes 好像有改進空間

class Region(models.Model):
    # ...
    def get_contained_remotes(self):
        return Remote.objects.filter(
            location__isnull=False,
            location__contained=self.boundary,
        )

    def get_visible_remotes(self, user):
        remotes = self.get_contained_remotes().get_viewable(user=user)
        return remotes

Well.

@cropse
Copy link

cropse commented Mar 15, 2017

直接繼承Manager這樣做應該也可以?
還是繼承QuerySet下來有別的好處?


class RestaurantManager(models.Manager):
    def some(self):
        return super(RestaurantQuerySet,self).filter(is_fastfood=False, average_price__gte=100)


class Restaurant(models.Model):
    is_fastfood = models.BooleanField()
    average_price = models.IntegerField()
    objects = RestaurantManager()

# In view.
some_restaurants = Restaurant.objects.some()

@uranusjr
Copy link
Owner Author

繼承 queryset 有個好處是串其他的條件比較方便。像這樣:

Restaurant.objects.filter_fastfood().filter_cheap()

就一定要把至少把其中一個定義在 QuerySet。所以為了方便起見,這種東西通常都是定義在 QuerySet,然後再用 as_manager()Manager.from_queryset() 建立對應的 manager class。

@cropse
Copy link

cropse commented Mar 19, 2017

了解,沒想到可以這樣用。

@uranusjr uranusjr mentioned this issue Jun 20, 2017
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants