According to django docs the best way of handling choice fields in models is do like this:
class Book(models.Model):
AVAILABLE = 'available'
BORROWED = 'borrowed'
ARCHIVED = 'archived'
STATUS = [
(AVAILABLE, 'Available to borrow'),
(BORROWED, 'Borrowed by someone'),
(ARCHIVED, 'Archived - not available anymore'),
]
# […]
status = models.CharField(
max_length=32,
choices=STATUS,
default=AVAILABLE,
)
However, my app grew a lot and model inter-connectivity between different app extended. In some cases I need to import the model class just to use the constant values
eg:
# some other file
from book.models import Book
def do_stuff_if_borrowed(book):
if (book.status == Book.BORROWED):
# do stuff
pass
I'd like to avoid importing models in other file as much as possible because it causes a lot of circular import issues and overall I feel it is not the cleanest way. I checked quickly and didn't find anybody presenting a solution on how to externalize these model constants like:
book/constants.py
AVAILABLE = 'available'
BORROWED = 'borrowed'
ARCHIVED = 'archived'
STATUS = [
(AVAILABLE, 'Available to borrow'),
(BORROWED, 'Borrowed by someone'),
(ARCHIVED, 'Archived - not available anymore'),
]
book/models.py
from .constants import STATUS, AVAILABLE
class Book(models.Model):
status = models.CharField(
max_length=32,
choices=STATUS,
default=AVAILABLE,
)
and then if I need only constants I don't need to imports Book model but only constants.py
Is there a reason not to do like this?
I am working on a project where we are considering something like this. It is connected to another service API which has metrics and definitions that we pull. So we had considered just pulling these files routinely and storing them in something like a metrics.json file and than using them in the models.
This is pretty fluid and nice, but I think what you lose is visibility to changes in models that result in migrations needing to be created, tracked and run.
In your instance, it seems like the constants won't really be changing and the intention is just to be DRY. It seems like you could create that constants file and then add a method the returns prefixed statuses for you model if you did want to have them be more distinct for each model.
What you sacrifice in this instance is easily referencing those values directly from the model. In the example from the docs you could reference the status using Book.STATUS_BORROWED. But in your example, you would have to actually import those constants separately.
I think the best solution is just to make a mixin that covers this and have all of your models that need these statuses inherit from that.
class BorrowableBookMixin(models.Model):
STATUS_AVAILABLE = 'available'
STATUS_BORROWED = 'borrowed'
STATUS_ARCHIVED = 'archived'
STATUS_CHOICES = [
(STATUS_AVAILABLE, 'Available to borrow'),
(STATUS_BORROWED, 'Borrowed by someone'),
(STATUS_ARCHIVED, 'Archived - not available anymore'),
]
status = models.CharField(
max_length=32,
choices=STATUS_CHOICES,
default=STATUS_AVAILABLE,
)
class Meta:
abstract = True
Related
The below model have EditorManager,
class EditorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='E')
class Person(models.Model):
first_name = models.CharField(max_length=50)
role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
people = models.Manager()
editors = EditorManager()
If I query Person.objects.filter(role='E') or Person.editors.all() I gets same result.
then, Why do we go for writing EditorManager() ?
The above code is from Django documentation (https://docs.djangoproject.com/en/3.0/topics/db/managers/).
As mentioned in the Documentation:
using multiple managers on the same model. You can attach as many Manager() instances to a model as you’d like. This is a non-repetitive way to define common “filters” for your models.
Since you just have one action, it may be hard for you to see the benefits. However, as your code gets larger, say:
good = Book.objects.filter(author="PersonA", stars=5).order_by("-date_created").exclude(outdated=True)
normal = Book.objects.filter(author="PersonA", stars=3).order_by("-date_created").exclude(outdated=True)
bad = Book.objects.filter(author="PersonA", stars=1).order_by("-date_created").exclude(outdated=True)
You can see that's an awful lot of code. With managers, you can do something like this:
class AuthorAManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author="PersonA").order_by("-date_created").exclude(outdated=True)
class Book(models.Model):
# ...
author_a = AuthorAManager()
good = Book.author_a.filter(stars=5)
normal = Book.author_a.filter(stars=3)
bad = Book.author_a.filter(stars=1)
Overall, it can make your code look a lot cleaner and understandable. As you said, you can't see the difference right now as you haven't gone into complex/repeating handles, but as your project expands, I'd say it's a worthwhile investment.
I'm new to Django and I'm facing a question to which I didn't an answer to on Stackoverflow.
Basically, I have 2 models, Client and Order defined as below:
class Client(models.Model):
name = models.CharField(max_length=200)
registration_date = models.DateTimeField(default=timezone.now)
# ..
class Order(models.Model):
Client = models.ForeignKey(ModelA, on_delete=models.CASCADE, related_name='orders')
is_delivered = models.BooleanField(default=False)
order_date = models.DateTimeField(default=timezone.now)
# ..
I would like my QuerySet clients_results to fulfill the 2 following conditions:
Client objects fill some conditions (for example, their name start with "d" and they registered in 2019, but it could be more complex)
Order objects I can access by using the orders relationship defined in 'related_name' are only the ones that fills other conditions; for example, order is not delivered and was done in the last 6 weeks.
I could do this directly in the template but I feel this is not the correct way to do it.
Additionally, I read in the doc that Base Manager from Order shouldn't be used for this purpose.
Finally, I found a question relatively close to mine using Q and F, but in the end, I would get the order_id while, ideally, I would like to have the whole object.
Could you please advise me on the best way to address this need?
Thanks a lot for your help!
You probably should use a Prefetch(..) object [Django-doc] here to fetch the related non-delivered Orders for each Client, and stores these in the Clients, but then in a different attribute, since otherwise this can generate confusion.
You thus can create a queryset like:
from django.db.models import Prefetch
from django.utils.timezone import now
from datetime import timedelta
last_six_weeks = now() - timedelta(days=42)
clients_results = Client.objects.filter(
name__startswith='d'
).prefetch_related(
Prefetch(
'orders',
Order.objects.filter(is_delivered=False, order_date__gte=last_six_weeks),
to_attr='nondelivered_orders'
)
)
This will contain all Clients where the name starts with 'd', and each Client object that arises from this queryset will have an attribute nondelivered_orders that contains a list of Orders that are not delivered, and ordered in the last 42 days.
I'm getting rather tired of paging through lots of irrelevant little fiddly properties while looking for the actual database structure of my models. Would it be a bad thing to use proxy models universally just to keep my code better organized / more readable? I.e.
class Foo_Base( models.Model):
title = models.CharField( ...)
# other DB fields. As little as possible anything else.
class Bar_Base( models.Model):
foo = models.ForeignKey( Foo_Base, ... )
etc. not many more lines than there are columns in the DB tables. Then at the bottom or elsewhere,
class Foo( Foo_Base):
class Meta:
proxy=True
#property
def some_pseudo_field(self):
# compute something based on the DB fields in Foo_Base
return result
#property
# etc. pages of etc.
The fact that makemigrations and migrate tracks proxy models makes me slightly concerned, although this usage seems to be exactly what the Django documentation says they are for (wrapping extra functionality around the same database table).
Or is there another way to organize my code that accomplishes the same (keeping fundamental stuff and fiddly little support bits apart).
[Edit] am offering up something that seems to work as a self-answer below. I'd still very much like to hear from anybody who knows for a fact that this is OK, given the deep Django magic on its declarative field declarations.
(About the only thing I dislike about Python, is that it does not have include functionality for reading in a heap of code from another file! )
I think I may have found an answer: use a plugin class inheriting from object,
as is commonplace for class-based Views.
I'd still very much like to hear from anybody who knows for a fact that this is OK, given the deep Django magic on its declarative field declarations.
Minimal proof of concept:
class PenMethods1( object):
#property
def namey(self):
return format_html('<div class="namey">{name}</div>', name=self.name )
class PenTest1(PenMethods1, models.Model):
name = models.CharField( max_length=16, blank=True )
def __repr__(self):
return f'<Pentest1 object id={self.id} name={self.name}>'
Initial migration was OK. Then I added
pnum = models.ForeignKey( 'Pennum', models.SET_NULL, null=True)
(Pennum was something already lying around in my playpen) and ran makemigrations and migrate. Again OK and basic functioning checks out...
>>> from playpen.models import PenTest1, Pennum
>>> n = Pennum.objects.last()
>>> n
<Pennum object id=3 name="t3" num=14 >
>>> p = PenTest1.objects.get(name='bar')
>>> p
<Pentest1 object id=2 name=bar>
>>> p.namey
'<div class="namey">bar</div>'
>>> p.pnum=n
>>> p.save()
>>> n=Pennum.objects.last()
>>> list(n.pentest1_set.all())
[<Pentest1 object id=2 name=bar>]
>>>
I am using Relay, Django, Graphene Graphql.
I would like to use django_filters to filter for multiple arguments of type on accommodation. This is described in my schema file and atm looks like:
class AccommodationNode(DjangoObjectType) :
class Meta:
model = Accommodation
interfaces = (relay.Node,)
filter_fields = ['type']
This works perfectly if I pass a single string like: {"accommodationType": "apartment"}, but what if I want to filter for all accommodations that are apartments OR hotels? something like: {"accommodationType": ["apartment","hotel"]}
This is my model:
class Accommodation(models.Model):
ACCOMMODATION_TYPE_CHOICES = (
('apartment', 'Apartment'),
('host_family', 'Host Family'),
('residence', 'Residence'),
)
school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='accommodations')
type = models.CharField(
max_length=200,
choices=ACCOMMODATION_TYPE_CHOICES,
default='apartment'
)
def __str__(self):
return str(self.school) + " - " + self.type
Is there any way I can do this without writing custom filters as are suggested here? For only one filter field this is a great solution but I'll end up having around 50 throughout my application including linked objects...
Have a look at Django REST Framework Filters:
https://github.com/philipn/django-rest-framework-filters
It supports more than exact matches, like in, which you are looking for, but also exact, startswith, and many more, in the same style of Django's ORM. I use it frequently and have been impressed - it even integrates with DRF's web browseable API. Good luck!
like FlipperPA mentioned, I need to use 'in'. According to the django_filter docs:
‘in’ lookups return a filter derived from the CSV-based BaseInFilter.
and an example of BaseInFilter in the docs:
class NumberRangeFilter(BaseInFilter, NumberFilter):
pass
class F(FilterSet):
id__range = NumberRangeFilter(name='id', lookup_expr='range')
class Meta:
model = User
User.objects.create(username='alex')
User.objects.create(username='jacob')
User.objects.create(username='aaron')
User.objects.create(username='carl')
# Range: User with IDs between 1 and 3.
f = F({'id__range': '1,3'})
assert len(f.qs) == 3
The answer to my question:
class AccommodationNode(DjangoObjectType) :
class Meta:
model = Accommodation
interfaces = (relay.Node,)
filter_fields = {
'type': ['in']
}
With the argument {"accommodationType": "apartment,hotel"} will work
Edit: I would very much like to accomplish this without installing a 3rd-party app. It seems simple/common enough that someone would have posted a line of code that accomplishes this by now?
Couldn't this be done easily in SQL? Would it be taboo to just hit the DB with a custom SQL in the index view?
So I have a parent Class and 2 child Classes. I would like to query all items and return a quick list.
from django.db import models
VIDEO_TYPE_CHOICES = (
('dvd', 'DVD'),
('downloaded', 'Downloaded'),
)
BOOK_TYPE_CHOICES = (
('e_book', 'E-Book'),
('print', 'Print'),
('audio', 'Audio Book'),
)
class Unit(models.Model):
name = models.CharField(max_length=200)
image = models.ImageField()
def __unicode__(self):
return self.name
class Video(Unit):
this_type = models.CharField(max_length=20, choices=VIDEO_TYPE_CHOICES, default='dvd')
run_time = models.CharField(max_length=200)
class Book(Unit):
this_type = models.CharField(max_length=20, choices=BOOK_TYPE_CHOICES, default='print')
pages = models.CharField(max_length=200)
All I want to do is display a list of all "Units" with this_type mushed in there on my index page.
Such as:
Lord Of The Rings, lotr.jpeg, DVD
Treasure Island, treasure_island.jpeg, Print
But I only have access to the Units name and image properties if I do a standard "gimme all Units" query...not this_type. Unless of course I make an assumption about the object and try object.book.this_type for example...which throws an exception if that particular object is not a Book.
I've been researching this for a while now...and while I can find several related questions and several possible methods (generic relations, for example?), I cannot find an example that I can relate to my own use case...or understand at all for that matter. I've only been at this stuff (Python and Django) for about a week now...I learn best when I can just make something work, get an understanding of all the moving parts, and then build on that understanding.
In that light, if someone could give me an example of how to generate the previously mentioned object list, I would be extremely grateful!
Pretty PLS???
I would recommend using the django app model_utils
OOP is generally not the best design pattern for models but if you are going to go that route model_utils has an InheritanceManager which does exactly what you want.