i have three classes. Place is foreignkey of Activity(where the activity takeplace), Park and Restarant are inherited from Place.
class Activity(Model):
place=ForeignKeyField('Place')
....
class Place(Model):
address=CharField(max_length=200)
....
class Park(Place):
ticket_price=Decimal()
....
class Restarant(Place):
manager_name=CharField(max_length=100)
how can i get the real type of 'Place' in a query on Activity,so i can get the additional attributes of child type.
activity_list= Activity.objects.all()
all the activity_list[n].place are 'Place' ,neither Park or Restarant, so i can't get 'ticket_price' or 'manager_name' directly.
i have checked the model-utils, and can't find a easy way to do this.
any help will be appreciated.
inspect.getmro(cls)
Return a tuple of class cls’s base classes, including cls, in method resolution order. No class appears more than once in this tuple. Note that the method resolution order depends on cls’s type. Unless a very peculiar user-defined metatype is in use, cls will be the first element of the tuple.
try:
import inspect
for activity in activity_list:
print inspect.getmro(activity.place)
or try :
for activity in activity_list:
print activity.place._type()
try:
activity_list= Activity.objects.all()
for act in activity_list:
for b in act.place.__class__.__bases__:
print b.__name__
I use Django Inheritance Managers for this, from django-model-utils. It allows you to do exactly what you want...you said you checked model-utils to do this, but it didn't work. It should work -- what did you try with django-model-utils? You need to add this line to the parent model of Place:
objects = InheritanceManager()
I haven't tried this with your exact model design, but have you tried something like:
places = Activity.objects.all().values_list('place_id', flat=True)
places_of_right_type = Place.objects.filter(pk__in=places).select_subclasses()
?
------ UPDATE --------
Not sure this will work, but throwing it out there as an idea...would only work for a get(), not all():
places = Activity.objects.get(pk=#).place.objects.select_subclasses()
The docs say that you can call select_subclasses() on the InheritanceManager itself, which is stored in .objects.
Related
I have a enum table ServiceType in my database which includes "Credit", "Investment" and "Insurance" and another table Service which have two columns - type_id and type. Is there a way to call method which will be generating value of type from value of type_id while initialization?
Should I use __init__ or maybe save?
type in Service is a ForeignKey to ServiceType
Thanks, sorry for my bad English.
So, I've tried something like this:
class Service(models.Model):
type_id = models.CharField(..)
type = models.ForeignKey('ServiceType',
to_field = 'type',
default = foo(type_id))
class ServiceType(models.Model):
type = models.CharField(..)
I've found the answer if anyone will be looking for it in future.
Things like that are managed by using signals. For above example I've used pre_save signal in function decorated as reciver.
In my understanding, reciver catches data before or after saving in database - way of doing it is based on type of signal. Those functions could basicly do anything, for example - change type of Service based on given type_id.
I want to be able to have a foreign key to a parent class, thereby allowing queries of the children classes as well. All other solutions are nightmarish.
I have tried to make this (Destination is also a parent class that has a # of children classes I want to relate to):
class Destination(PolymorphicModel)
class Account(Destination)
class Organization(Destination)
class Person(Destination)
class Transaction(models.Model)
destination = models.ForeignKey(Destination, verbose_name="Destination", null=True, blank=True,
related_name="CompletedTransaction_Destination_FK")
I am referencing destination in other places as well.
This is the error message I get when I try to migrate:
psycopg2.IntegrityError: could not create unique index "baseapp_organization_organization_destination_ptr_id_key"
DETAIL: Key (organization_destination_ptr_id)=(1) is duplicated.
I would love it if I could make the destination class
abstract = True
but then I can't have a foreign key. I need to be able to choose all of those destinations, and they need to remain distinct, real models in the database.
I have also tried GenericRelations, but that proved to be a nightmare as I said earlier.
It feels like I could just get around this error somehow though, any help?
The solution, and I'm guessing it would apply to other errors where the index could not be created, was to wipe the database an delete all the migrations. It was a pain to be sure, but now I can do:
destinations = Destination.objects.all()
and it will give me all the objects, as according to django polymorphic
I have some models using django-polymorphic-model
eg.
class Article(PolymorphicModel):
...
class Blog(Article):
tags = ...
class Story(Article):
publish = ...
Normally if I get all articles, I just do Article.objects.all(), however what if I want to get all articles that tags are empty? If I do Articles.objects.filter(tags__isnull=True) it will break because other models don't have this field, I would like to include Story entries too, do I really have to split into 2 different queries and combine again?
OK After some digging for the documentation through issues, here is how to do it
Articles.objects.filter(Blog___tags__isnull=True)
Hope you found an answer before.
Articles.objects.filter(Q(Blog___tags__isnull=True)|Q(Story__tags__isnull=True))
My Post model have list of authors id
class Post(Document):
authors_id = ListField(IntField(required=True), required=True)
But sometime I need to use default Django User class. How most rapidly I can do it?
(I'm using sqlite for users and sessions and MongoDB (mongoengine ODM) for other. Don't ask why:))
I was tried to write it:
def get_authors(self):
authors = list()
for i in self.authors_id:
authors.append(get_user(IntField.to_python(self.authors_id[i])))
return authors
...and it raises 'list index out of range' exception. (authors id not empty, really). What I'm doing wrong?
Not sure about the to_python method but since you are looping through the authors_id, there is no need to do
authors.append(get_user(IntField.to_python(self.authors_id[i])))
You should be good with
authors.append(get_user(IntField.to_python(i)))
Instead of
IntField.to_python(self.authors_id[i]))
I think you just need to do:
IntField.to_python(i)
In Python the 'for i in some_list' construction gives you elements of the list, not integer indexes.
You said that you were getting this error:
and unbound method to_python() must be called with IntField instance as first argument (got int instance instead)
I got a similar error from MongoEngine. In my case the problem was that I defined the field like this:
foo_id = IntField
The correct way to define it is:
foo_id = IntField()
When I added the parenthesis, the problem went away.
I am frustrated that in Django I often end up having to write methods on a custom Manager:
class EntryManager(Manager):
def filter_beatle(self, beatle):
return self.filter(headline__contains=beatle)
... and repeat pretty much the same method in a different Manager for a reverse query:
class BlogManager(Manager):
def filter_beatle(self, beatle):
return self.filter(entry__headline__contains=beatle)
... and a predicate on Entry:
def headline_contains(self, beatle):
return self.headline.find(beatle) != -1
[Note that the predicate on Entry will work on Entry objects that haven't even been saved yet.]
This feels like a violation of DRY. Is there some way to express this once and use it in all three places?
What I would like to be able to do is write something like:
q = Q(headline__contains="Lennon")
lennon_entries = Entry.objects.filter(q)
lennon_blogs = Blog.objects.filter(q.reverse(Entry))
is_lennon = entry.would_filter(q)
... where 'headline__contains="Lennon"' expresses exactly once what it means to be 'an Entry about "Lennon"', and this can be used to construct reverse queries and a predicate.
The best place for this is a custom manager. According to django's guidelines a manager class is the best place for code that is affecting more than one object of a class.
class EntryManager(models.Manager):
def filter_lennons(self):
return self.get_query_set().filter(headline__contains='Lennon')
class Entry(models.Model):
headline = models.CharField(max_length=100)
objects = EntryManager()
lennons = Entry.objects.filter_lennons()
You should never rarely have to do the following:
if entry.headline.find('Lennon') >= 0:
because the filter should take care of restricting the result set to the instances you're interested in.
If you're going to be using the same filter multiple times, you can create a custom manager or a simple class method.
class Entry(models.Model):
...
# this really should be on a custom manager, but this was quicker to demonstrate
#classmethod
def find_headlines(cls, text):
return cls.objects.filter(headline__contains=text)
entries = Entry.find_headlines('Lennon')
But really, the DRYness has already been contained within the Queryset API. How often are you really going to be hard coding the string 'Lennon' into a query? Usually, the search parameter will be passed into a view from a GET or POST. Perfectly DRY.
So, what is the actual problem? Other than exploring the queryset API, have you ever had to hard code lookup values in multiple queries like your question?
For the "reverse filter" case you can use a subquery:
Blog.objects.filter(entries__in=Entry.objects.filter_beatle("Lennon"))
Reusing or generating predicates is not possible (in general) as there are predicates that cannot be expressed as queries and queries that cannot be expressed as predicates without db access.
My most common use for the predicate seems to be in asserts. Often something like:
class Thing(Model):
class QuerySet(query.QuerySet):
def need_to_be_whacked():
# ... code ...
def needs_to_be_whacked(self):
return Thing.objects.need_to_be_whacked().filter(id=self.id).exists()
def whack(self):
assert self.needs_to_be_whacked()
for thing in Thing.objects.need_to_be_whacked():
thing.whack()
I want to make sure that no other code is calling whack() in state where it doesn't need to be whacked. It costs a database hit, but it works.