Two-way querying in Django without circular reference - python

Let's say you have two models: Post and Category. Each Post has a category_id.
Getting a post's category is straightforward: post.category. What if you want to get all the posts for a certain category? I suppose you would do
def posts(self):
return Post.filter(category__pk=self.id)
But what if the Post model and Category model are in separate files? Because Post and Category now require each other, you would end up with a circular reference.
Maybe you say the solution is to put Post and Category into the same file. But what if your app has 50 different models, many of them quite large, all in separate files? Should you combine Post and Category into one file and leave all the others separate? Should you combine all 50 models into one gigantic file?
I'm hoping to find one of two things:
An answer to this problem that doesn't involve combining files
A good, logical reason for grouping models into the same file with one another. All my models are related to some extent, so where do you draw the line as far as grouping goes? If you draw the line with foreign keys, all my models would end up in the same file.

For part 1. of your question:
Django automatically sets up the reverse relationship for you. on your Category you have access to a post_set attribute, which is itself a Manager, so you can do:
def posts(self):
return self.post_set.all()
Check out the docs for more on this.
For part 2. I have a less complete answer... If you are experiencing a need to break out models into so many files you probably have a more fundamental problem. I'd say you should think about breaking down that huge app into some smaller ones.
I think the best advice I've had in terms of reducing the size of my apps is to "do one thing". If your app can't be described in a relatively short phrase, it's worth considering how to break it out into a number of smaller apps - each of which will "do one thing".
That advice is, of course, rather loaded. It takes quite a bit of planning to figure out how to break down some complex tasks, and sometimes hindsight is the only way to see where things got out of hand. And refactoring something of the size of which you speak can be quite daunting. (If you can tell I'm speaking from experience!). My only advice in this case is to take it a step at a time. Try to break down what look like big, application-spanning issues into small, manageable chunks and do them when you can.
To address your immediate need, I'd suggest grouping your models into files topically or categorically. It might lead you in the direction of refactoring this one (what sounds like a mammoth) app into a number of smaller apps. I think that's the direction you should really be heading with this.

You can refer to models by their name (i.e., a string) rather than the actual object. Are they in separate applications? In which case you can refer to them using dot notation as described in the documentation on ForeignKey:
#in mysite/categories/models.py
class Category(models.Model):
...
#in mysite/posts/models.py
class Post(models.Model):
category = models.ForeignKey('categories.Category')
You can also import the model within a function, rather than at the top of the file. I often do that to avoid circular references:
def posts(self):
from posts.models import Post
return Post.filter(category__pk=self.id)

Related

Django logic and where to put it?

I am new to Django, but I recently created my first application. I am wondering if I put my logic in the wrong places. From the Django book, I got that logic should but put into the views and data in models. But I have recently read that views should be as small as possible and let models handle the logic. My problem is my views handle all my logic while my models only handle data going to and from my database. Have I messed up when creating this app, and if so, how would I fix it?
Django's philosophy / best practices encourage "fat models thin controllers" (controllers being Views in Django). Having tried both ways, it definitely works better with the "fat models" approach. Keeping the logic as close to the data models makes it more reusable and you can there are many features in Django that work better that way.
One example would be returning a paginated list view. If you need to calculate something for every object in a queryset, you could
loop over it in the view doing the calculation
or you could add a model method then call it on every iteration in the template.
Looping over the queryset in the view will do the calculation on the whole queryset - not good if you are only showing 10 objects from a list of 1000.
Calling a model method from the template, the calculation will only be done on the 10 objects on that page.
Obviously you can add some more code to the view to only do that calculation on the objects on that page, but then thats extra code that isn't needed if you go the other route. If you need the same calculation on another page, keeping the logic in a model method will be reusable without any alteration, while you will need to cut and paste it in the view, or create a new method. While its not a huge difference, lots of small things like this start to add up.
It is better to have "Fat Models, Skinny Views." Many of Django experts will give you this tip. Google this phrase and you will find some resources that are saying "Fat Models, Skinny Views," or "Fat Models, Skinny Controllers." By the way, Django creators named Controllers as Views, and Views as Templates which maybe will cause some misunderstanding while reading articles about MVC which is MTV in Django.
No you haven't mess it up. You can find your solution through experience (years of developing).
So, either using fat models and thin views or the opposite its up to you and your application requirements.
As you learn you'll discover new techniques and methods that will help you extend your "logic" and your app implementation.
At the beggining you'll make mistakes, but that's alright. We need them to become better developers, coders etc.
So my advice is: keep calm and learn (good practices)!
The way to do it is simple. And I would say that it is not actually messed up if your logic lies in views mostly.
The most repetitive functions and logic which you have used in views in multiple functions, you can create a function defined in model and then call that function in views. (you can do it step by step)
For example if it is a social network and you are setting the city of the user. Now as your app grows, possibly there will be many functions and many places through which city is being set. If you are doing it all in views repitively then you would be stuck at some point of time because to make smallest of changes, you might have to edit all the functions.
The good way would be to define set_cities(self, cities) in user model and call it whenever needed like user.set_cities(cities_list). So if you have to trigger other functions everytime city is being set (maybe like sending notifications or updating other tables), you have to define it once only in set_cities().
In any case, all logic cannot be only in models so do not worry much about it. Just keep on trying to simplify the views and keep on shifting any repetition of logic to models.

Number of attributes in Django Models

I searched a lot and did not find what I´am looking for.
What would be the best concept for a model class in django?
To extend User, would be better to have a class with several attributes, or break this class into several classes with few attributes? I´m using the django ORM now.
Say I have a class called Person that extends User, would be better:
class Person(models.Model):
user = foreingkey(User)
attribute1 =
...
attributeN =
Or, would it be better to do this:
class PersonContac(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
class PersonAddress(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
class PersonHobby(models.Model):
user = foreingkey(User)
attribute1 =
...
attribute3 =
My each of my views would use the data from the smaller classes (probably).
Over time, the atrribute number can expand.
I want to do is do it once, and touch the minimum possible.
Various attributes can be unfilled by the user, they are not required.
The number of user is indefinite (can be a lot).
I´m concerned in terms of long term performance and maintaining.
If someone can explain me, what would be better for my code, and why.
And what would be better in general (less classes/more attributes, or more classes/less attributes), using the Django ORM.
It is better if my views use the data of only one model class, or it makes no (or little) difference?
Edit:
On the rush for writing I used bad names on class. None of these attributes are many-to-many fields, the User will have only one value for each attribute, or blank.
The number of atributes can expand over time, but not in a great number.
Put any data that is specific to only one User directly in the model. This would probably be things like "Name", "Birthday", etc.
Some things might be better served by a separate model, though. For example multiple people might have the same Hobby or one User might have multiple Hobby(s). Make this a separate class and use a ForeignKeyField or ManyToManyField as necessary.
Whatever you choose, the real trick is to optimize the number of database queries. The django-debug-toolbar is helpful here.
Splitting up your models would by default result in multiple database queries, so make sure to read up on select related to condense that down to one.
Also take a look at the defer method when retrieving a queryset. You can exclude some of those fields that aren't necessary if you know you won't use them in a particular view.
I think it's all up to your interface.
If you have to expose ALL data for a user in a single page and you have a single, large model you will end up with a single sql join instead of one for each smaller table.
Conversely, if you just need a few of these attributes, you might obtain a small performance gain in memory usage if you join the user table with a smaller one because you don't have to load a lot of attributes that aren't going to be used (though this might be mitigated through values (documentation here)
Also, if your attributes are not mandatory, you should at least have an idea of how many attributes are going to be filled. Having a large table of almost empty records could be a waste of space. Maybe a problem, maybe not. It depends on your hw resources.
Lastly, if you really think that your attributes can expand a lot, you could try the EAV approach.

Django MVT design: Should I have all the code in models or views?

I'm pretty novice so I'll try to explain in a way that you can understand what I mean.
I'm coding a simple application in Django to track cash operations, track amounts, etc.
So I have an Account Model (with an amount field to track how many money is inside) and an Operation Model(with an amount field as well).
I've created a model helper called Account.add_operation(amount). Here is my question:
Should I include inside the code to create the new Operation inside Account.add_operation(amount) or should I do it in the Views?
And, should I call the save() method in the models (for example at the end of Account.add_operation() or must it be called in the views?)
What's the best approach, to have code inside the models or inside the views?
Thanks for your attention and your patience.
maybe you could use the rule "skinny controllers, fat models" to decide. Well in django it would be "skinny views".
To save related objects, in your case Operation I'd do it in the save() method or use the pre_save signal
Hope this helps
Experienced Django users seem to always err on the side of putting code in models. In part, that's because it's a lot easier to unit test models - they're usually pretty self-contained, whereas views touch both models and templates.
Beyond that, I would just ask yourself if the code pertains to the model itself or whether it's specific to the way it's being accessed and presented in a given view. I don't entirely understand your example (I think you're going to have to post some code if you want more specific help), but everything you mention sounds to me like it belongs in the model. That is, creating a new Operation sounds like it's an inherent part of what it means to do something called add_operation()!

Does Django support model classes that inherit after many non-abstract models?

Lets say I have three django model classes - lets call them A, B and C. If A and B are abstract, I can do something like:
class C(A,B):
pass
What if they aren't abstract and I do the same? Will everything still work correctly or no? Or have I got it wrong and this should not be done with abstract models either?
I'm having some issues which I'm attributing to the fact that the answer is probably no, but I'd still prefer to make sure about this if anyone knows :)
The specific use case I had for this is probably better served by Generic Relations (I only recently discovered their existence), so I guess it would be understandable if the Django team made a design decision like this (I can't see many people needing to do this). I'd just like to know for sure what the case is.
Edit 1 (after Dominic's answer)
Interesting... The problem we're having is a structure similar to IMDb (I think IMDb is a bit easier to understand than the topic matter we actually have, so I'll use them as an example). On IMDb they have pages for People and pages for Movies and both People and Movies have their own message boards.
We've ended up connecting message boards to the People and Movies by creating a model called MessageboardOwner (with only one attribute - the id added automatically by Django), which "owns" the message board and People and Movies inherit it. The problem is that our "People" class inherits from two other classes also. The class definition is something like:
class Person(A,B,MessageboardOwner):
Initially this seemed to work out fine, but then today something rather weird happened... I was deleting a Person in the admin and the admin asked the "Are you sure?" question and was showing me what other objects it would have to delete. It was trying to delete two message boards, not one. One of these message boards should have been owned by a Movie, not a Person.
Upon looking at what exactly was in the database, I found that this Person instance was using the same MessageboardOwner instance as the Movie was. When I played around with it, what came out was that the Movie class, which inherited only after MessageboardOwner, seemed to work ok. Saving the Person, however, only created a MessageboardOwner object if one didn't already exist (or possibly overwrote the existing one - I'm not sure). I also found that the id fields inherited from A, B and MessageboardOwner were always equal, which seemed strange to me.
Yes, you can use normal Python multiple-inheritance with models. Bear in mind this warning though:
Just as with Python's subclassing,
it's possible for a Django model to
inherit from multiple parent models.
Keep in mind that normal Python name
resolution rules apply. The first base
class that a particular name (e.g.
Meta) appears in will be the one that
is used; for example, this means that
if multiple parents contain a Meta
class, only the first one is going to
be used, and all others will be
ignored.
Generally, you won't need to inherit
from multiple parents. The main
use-case where this is useful is for
"mix-in" classes: adding a particular
extra field or method to every class
that inherits the mix-in. Try to keep
your inheritance hierarchies as simple
and straightforward as possible so
that you won't have to struggle to
work out where a particular piece of
information is coming from.
From the Django docs.
Generally, multiple inheritance is a bad idea, and there are simpler ways to do things. If you flesh out what problem you're trying to solve a bit more clearly, we might be able to help a bit better.

models.py getting huge, what is the best way to break it up?

Directions from my supervisor:
"I want to avoid putting any logic in the models.py. From here on out, let's use that as only classes for accessing the database, and keep all logic in external classes that use the models classes, or wrap them."
I feel like this is the wrong way to go. I feel that keeping logic out of the models just to keep the file small is a bad idea. If the logic is best in the model, that's where it really should go regardless of file size.
So is there a simple way to just use includes? In PHP-speak, I'd like to propose to the supervisor that we just have models.py include() the model classes from other places. Conceptually, this would allow the models to have all the logic we want, yet keep file size down via increasing the number of files (which leads to less revision control problems like conflicts, etc.).
So, is there a simple way to remove model classes from the models.py file, but still have the models work with all of the Django tools? Or, is there a completely different yet elegant solution to the general problem of a "large" models.py file? Any input would be appreciated.
It's natural for model classes to contain methods to operate on the model. If I have a Book model, with a method book.get_noun_count(), that's where it belongs--I don't want to have to write "get_noun_count(book)", unless the method actually intrinsically belongs with some other package. (It might--for example, if I have a package for accessing Amazon's API with "get_amazon_product_id(book)".)
I cringed when Django's documentation suggested putting models in a single file, and I took a few minutes from the very beginning to figure out how to split it into a proper subpackage.
site/models/__init__.py
site/models/book.py
__init__.py looks like:
from .book import Book
so I can still write "from site.models import Book".
The following is only required for versions prior to Django 1.7, see
https://code.djangoproject.com/ticket/3591
The only trick is that you need to explicitly set each model's application, due to a bug in Django: it assumes that the application name is the third-to-last entry in the model path. "site.models.Book" results in "site", which is correct; "site.models.book.Book" makes it think the application name is "models". This is a pretty nasty hack on Django's part; it should probably search the list of installed applications for a prefix match.
class Book(models.Model):
class Meta: app_label = "site"
You could probably use a base class or metaclass to generalize this, but I haven't bothered with that yet.
Django is designed to let you build many small applications instead of one big application.
Inside every large application are many small applications struggling to be free.
If your models.py feels big, you're doing too much. Stop. Relax. Decompose.
Find smaller, potentially reusable small application components, or pieces. You don't have to actually reuse them. Just think about them as potentially reusable.
Consider your upgrade paths and decompose applications that you might want to replace some day. You don't have to actually replace them, but you can consider them as a stand-alone "module" of programming that might get replaced with something cooler in the future.
We have about a dozen applications, each model.py is no more than about 400 lines of code. They're all pretty focused on less than about half-dozen discrete class definitions. (These aren't hard limits, they're observations about our code.)
We decompose early and often.
I can't quite get which of many possible problems you might have. Here are some possibilities with answers:
multiple models in the same file
Put them into separate files. If there are dependencies, use import to pull in the
additional models.
extraneous logic / utility functions in models.py
Put the extra logic into separate files.
static methods for selecting some model instances from database
Create a new Manager in a separate file.
methods obviously related to the model
save, __unicode__ and get_absolute_url are examples.

Categories

Resources