Some days ago a guy explained me that on ruby on rails the queries are done on models. Because it gets already saved at your data before be requested on views and the query.
By the way I've learned and had been working until now, I'm setting the query on views.py and passing, through a context variable. So I started to read about Model.Manager and still didn't find a answer to which way is better:
queries made on views
queries made by simple functions on models
queries made on models.Manager class for each model
Use custom QuerySets and ModelManagers in your models
call your model's ModelManager custom methods from within your views
pass the returned values (querysets, instances or whatever) to the templates (or JSON serializer etc)
It's a matter of separation of concerns:
the template (or json serializer or whatever) doesn't have to know where data come from nor they were obtained - only what the data structure is
views don't have to know about how the query is implemented, only on how to get relevant data
only the model layer should know about it's implementation (encapsulation 101)
Every model is associated with a Manager (default one is objects)
>>> from django.contrib.auth.models import User
>>> user = User.objects.all()
>>> type(user)
<class 'django.db.models.query.QuerySet'>
When you need to make any queries to the model you need a manager for this. In the above example user doing a query on objects manger of User Model.
3-queries made on models.Manager class for each model - correct interpretation
Click here for Documentation
Ref.
By default, Django adds a Manager with the name objects to every Django model class.
If u have specific business logic, you can make use of managers to override built-in model methods like save() and delete() to add business logic to default database behaviour or you can specifically design some query logic.
file name ---- > models.py
from .managers import ModelNameManager
class ModelName(Base):
title = models.CharField(max_length=255, blank=True, null=True)
headline = models.CharField(max_length=255, blank=True, null=True)
objects = ModelNameManager()
create a file managers.py file in your application
file name ---- > managers.py
class ModelNameQuerySet(models.QuerySet):
def by_name(self, id):
return self.filter(id=id)
class ModelNameManager(models.Manager):
def get_queryset(self):
return ModelNameQuerySet(self.model, using=self._db)
def by_name(self, ad):
return self.get_queryset().by_name(id)
in Views.py or any services file file your query will be
import ModelName
obj = ModelName.get_queryset(id)
obj.title
this will return the object based on the query written in managers.
I hope this is helpful.
According to the Django idioms want to add some advice that can be useful:
model managers it's a place for most common queries, not for all queries. Managers describe basic methods for working with models. If you have a widely-used logic for your model with queryset - put it to the manager's method. For example, my users split by domains, so I want to get the user by name and domain, so add the get_by_name_and_domain method to the user model manager. Also, you can access your model by the model attribute in the manager.
models are the part of MTV (Model-View-Template) model on Django and the main purpose of the 'model' itself is to describe db-object but not related business logic. So put your custom queries to views and remember about DRY principle.
Related
I have model Post and i'm making querysets like this in view
user = CustomUser.objects.get(username=user)
blocked_users = user.blocked.all()
posts_except_blocked = Post.objects.exclude(author__in=blocked_users).order_by('-pub_date')
and a few similar. But i recognized that I can put this into model class as #staticmethod and now it's looking
#staticmethod
def get_posts_except_blocked(user):
user = CustomUser.objects.get(username=user)
blocked_users = user.blocked.all()
return Post.objects.exclude(author__in=blocked_users).order_by('-pub_date')
Should I put every queries like this to model?
I have there in views method
def create_notifications(post):
for word in post.content_post.split():
if '#' in word:
user_to_notificate = CustomUser.objects.get(username=word[1:])
TalkAbout(where=post, _from=post.author, to=user_to_notificate).save()
I'm calling it after save post form to create notifications. Can this be in views or maybe I need to put it in Post class too? Is it possible to call this function automaticly after creating post?
Yes you can put class related methods into Model class. Or you can use Model Manager.
A Manager is the interface through which database query operations are provided to Django models. At least one Manager exists for every model in a Django application. The way Manager classes work is documented in Making queries; this document specifically touches on model options that customize Manager behavior.
Manager in Django offical doc
What is Manager in Djnago stackoverflow answer
When showing {{ user }} in a Django template, the default behavior is to show the username, i.e. user.username.
I'm changing this to show the user's initials instead, which are stored in a separate (OneToOneField) UserProfile model.
So in customsignup/models.py I've overridden the __unicode__ function successfully, with the desired result:
# __unicode__-function overridden.
def show_userprofile_initials(self):
return self.userprofile.initials
User.__unicode__ = show_userprofile_initials
But of course, the database is hit again because it needs to independently select the UserProfile model every time a user object is asked to show itself as a string. So even though this works, it escalates the number of database hits quite a bit.
So what I'd like to do, is to automatically use select_related('userprofile') whenever a User model is called from the database, seeing that I will essentially always want the profile when dealing with the user in the first place.
In more technical terms, I'm attempting to override the model manager of an existing model. So I'm in no control over the User model definition itself, since that's in an imported library.
So I've tried overriding the objects member of the User model in the same way that I overrode the __unicode__ function, like so:
# A model manager for automatically selecting the related userprofile-table
# when selecting from user-table.
class UserManager(models.Manager):
def get_queryset(self):
# Testing indicates that code here will NOT run.
return super(UserManager, self).get_queryset().select_related('userprofile')
User.objects = UserManager()
Is this supposed to work? If so, what am I getting wrong?
(I will mark an answer as correct if it can show that this is not supposed to work in the first place.)
A similar question I've found is here, but it's approached from the other end:
Automatically select related for OneToOne field
No, User.objects = MyManger() is not supposed to work. According to the docs, there are just two supported methods for extending the provided auth User model, either a profile model, as you are doing, or a proxy model, which probably doesn't fit your case. From the docs (emphasis added):
There are two ways to extend the default User model without substituting your own model. If the changes you need are purely behavioral, and don’t require any change to what is stored in the database, you can create a proxy model based on User. This allows for any of the features offered by proxy models including default ordering, custom managers, or custom model methods.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
As an alternative to extending the provided auth User model, you can provide your own custom User model. Then you will have complete control over its managers.
Instead, consider simply replacing {{ user }} with {{ user.profile.initials }}. Creating the OneToOne field on your profile model also creates a reverse accessor for instances of the related model. You can specify the reverse accessor name by the related_name keyword argument on the profile model field. For example...
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model)
user = models.OneToOneField('auth.User', related_name='profile')
initials = models.CharField(max_length=6)
some_user = User.objects.first()
# assuming there is already a profile related to this user
some_user.profile.initials = 'S.P.Y.'
You could also make a __str__ method for your profile model like
def __str__(self):
return self.initials
Then when you do {{ user.profile }} in a template, the initials will be shown.
I am in the early stages of a project using Django, Django REST Framework, and SQL. I am very new to DRF.
I have a model that tracks user info for a game service that runs different servers for regions of the world (ex. NA, EU, etc). User IDs are only unique per-region, but the users are all stored using the same model (table). I am employing unique_together = ('user_id', 'region') in my model's Meta class to ensure there are no duplicates. Please note that, as such, the PKs in the DB are not related to the user IDs.
DRF, by default, would create endpoints using the DB's PKs of Users, but I have changed that to use a system like /users/na/123 to get the object where user_id = 123 and region = 'na' (north america). A snippet for this from urls.py follows:
url(r'^users/(?P<region>.+)/$', UserList.as_view()),
url(r'^users/(?P<region>.+)/(?P<user_id>.+$)', UserDetail.as_view()),
These are generic views (generics.ListAPIView and generics.RetrieveAPIView), respectfully.
Currently, the rest of my views are ViewSets.
One of the things I model is historical match data, where users are related to by a Game model, to keep track of who participated in a match like so:
class Game(models.Model):
player_1 = models.ForeignKey(User)
player_2 = models.ForeignKey(User)
I plan on implementing a route for games like I did w/users (again, game_id is unique only per-region) so I can do /game/<region>/<game_id>.
My question is this:
How can I get hyperlinks to Users using my established /user/<region>/<user_id> routes in Game list/detail views on the API?
Presently, my GameSerializer is defined as follows:
class GameSerializer(serializers.ModelSerializer):
class Meta:
model = Game
exclude = ('id',)
When I change it to a HyperlinkedModelSerializer I get the following error upon visiting the Game endpoints:
Could not resolve URL for hyperlinked relationship using view name
"user-detail". You may have failed to include the related model in
your API, or incorrectly configured the lookup_field attribute on
this field.
I assume this is because my User endpoints are implemented differently than what it expects (it can't know I have abandoned the default PK indexing method and opted for a custom route a la /users/<region>/<user_id> instead of /users/<pk>, right?)
How do I approach this problem? I would be open to suggestions that are extraneous to the DRF side of things, like restructuring my DB/Django models, if it seems like the direction I want things to go is crazy (not wanting to use PKs).
After a few more days of reading and thinking about the problem differently, it looks Meta.unique_together is kind of like expressing a composite key in SQL. This lead me to this solution:
https://groups.google.com/forum/#!topic/django-rest-framework/tHmEAzSNgG4
e.g. instead of using an URL like this to identify an employee:
api/1.3/employee/5/
I use an URL like this:
api/1.3/company/23/employee/5/
I use a HyperlinkedModelSerializer to serialise this model. I
couldn't find a way of configuring a HyperlinkedIdentityField to
handle the composite key (you can only specify a single lookup_field)
so I override the url with a SerializerMethodField instead, like this:
class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.SerializerMethodField('get_employee_detail_url')
def get_employee_detail_url(self, obj):
# generate the URL for the composite key
...
return composite_key_url
Still exploring my options, but this looks pretty clean.
I just came up against the same problem today. After going through the Django-Rest-Framework documentation on Generic views I came across:
lookup_field - The model field that should be used to for performing object lookup of individual model instances. Defaults to 'pk'. Note that when using hyperlinked APIs you'll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value.
http://www.django-rest-framework.org/api-guide/generic-views
In my case I did this in my models.py
class UserDetailView(generics.RetrieveAPIView):
model = User
serializer_class = UserSerializer
lookup_field = "username"
...and works lovely now. Hope that helps.
I want to use two different models for django.contrib.auth module. The first one is the default User model provided by Django which is completely suitable for admin access (groups, permissions etc.) but the other one is customer model which has a lot of different attributes (city, locale, address etc.) compared to default User model. These user groups must use different tables and mustn't have any relation.
I created a Customer model inherited from AbstractBaseUser and a middleware class called ChangeBaseUser like this:
class ChangeBaseUser(object):
def process_request(self, request):
match = resolve(request.path)
if match.app_name == "myapp":
settings.AUTH_USER_MODEL = 'myapp.Customer'
else:
settings.AUTH_USER_MODEL = 'auth.User'
It's working but I'm not sure whether this is the proper way to do it because in documentation there is a section (link) that implies the convenient way is to assign a static value for default user model.
If this is not the proper way, do you have any suggestions for having multiple user models per module basis?
If your requirement is to keep admin users and customers separate, I don't see anything wrong with having multiple user models. At this point, the customer model is like any model, except it is very similar to the user model and that is perfectly fine if that works for you. The only disadvantage is that you will have to possibly duplicate many helpers django gives you for the Django user model such as auth backend or sessions for users. If you are willing to do all that, this seems perfectly fine.
If you wish however to utilize many of the django helpers you might want to create a very basic user model which will serve as a base for both admins and customers:
class User(AbstractBaseUser):
# use this for auth and sessions
class Admin(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other admin-specific fields
class Customer(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other customer-specific fields
This will allow you to reuse many of the things Django provides out of the box however it will incur some additional db overhead since more joins will have to be calculated. But then you can cache things for customers so you can get some of the performance back.
I have these models:
class Company(models.Model):
name=models.CharField(max_length=100)
description=models.TextField()
#some more fields
class Product(models.Model):
name=models.CharField(max_length=100)
company=models.ForeignKey(Company)
#some more fields
class Category(models.Model):
parent=models.ForeignKey('self',null=True,blank=True)
name=models.CharField(max_length=100)
products=models.ManyToManyField(Product,null=True,blank=True)
#some more fields
as U can see each company has a list of product and each product belongs to some categories,I'm going to get the list of categories of each company using company pk,what's the best practice?should I define a database view?how can I do this?
Note:I've not ever used database view in django,I searched about it and that doesn't sound easy to me!
I always try to avoid using database views, stored procedures and in general stuff that 'lives' in the database itself rather than in the application code-base for the simple reason that it is very hard to maintain (and also you say good bye to database agnostic applications).
My advice here is to stick with django orm (which can do a lot) and only if you unable to get decent performances or if you need some advanced feature available through stored procedures/views only then to go for that solution.
Using views in django is quite easy.
Say you have 1 view to query, you create the view on the db then you write the model with fields matching the view' columns (name and type).
UPDATE:
You then need to set the table name as the view name in meta class definition.
After that you need to tell django not to write on that and to not try to create a table for the view model, luckily there is a conf for that:
class ViewModel(models.Model):
... view columns ...
class Meta():
db_table = 'view_name'
managed = False
I've no idea why you think you need a db view here. Generally, you don't use them with Django, since you do all the logic in Python via the ORM.
To get the list of categories for a company, you can just do:
categories = Category.objects.filter(products__company=my_company)
where my_company is the Company instance you're interested in.