I have long looked for an answer to this question, and have yet to find one. Hopefully s/o can help me out.
I have two models:
class UsedEverywhere(models.Model):
fk_OtherThing = models.ForeignKey(OtherThing)
...(many more fks)
class NewThing(models.Model):
fk_UsedEverywhere(UsedEverywhere)
newThingMember = models.IntegerField()
I simply want to join those two tables. It would be quite easy if I could just do something like
x = NewThing.objects.get()
x.fk_UsedEverwhere.fk_OtherThing ...
the problem is, the UsedEverywhere models is already used in many many places throughout my code base, and if i want to put 'NewThing' as the 'parent' of UsedEverywhere, I will have to update lots and lots of code so existing code that has something like:
x = x.filter(fk_OtherThing=val)
would have to change to
x = x.filter(fk_UsedEverywhere.fk_OtherThing=val)
and I am trying to avoid that. My SQL relationship are all set up exactly how I want them, i do not want to give UsedEverywhere a column for NewThing.
I want to do something like:
x = UsedEverywhere.objects.join(NewThing.fk_UsedEverywhere)
member = x.newThingMember
I found the solution:
Use a OneToOneField not a foreign key field https://docs.djangoproject.com/en/1.4/topics/db/examples/one_to_one/
As you can see in their examples, you can do something like
x = UsedEverywhere.objects.get(pk=1)
x.NewThing...
Related
I've got django 1.8.5 and Python 3.4.3, and trying to create a subquery that constrains my main data set - but the subquery itself (I think) needs a join in it. Or maybe there is a better way to do it.
Here's a trimmed down set of models:
class Lot(models.Model):
lot_id = models.CharField(max_length=200, unique=True)
class Lot_Country(models.Model):
lot = models.ForeignKey(Lot)
country = CountryField()
class Discrete(models.Model):
discrete_id = models.CharField(max_length=200, unique=True)
master_id = models.ForeignKey(Inventory_Master)
location = models.ForeignKey(Location)
lot = models.ForeignKey(Lot)
I am filtering on various attributes of Discrete (which is discrete supply) and I want to go "up" through Lot, over the Lot_Country, meaning "I only want to get rows from Discrete if the Lot associated with that row has an entry in Lot_Country for my appropriate country (let's say US.)
I've tried something like this:
oklots=list(Lot_Country.objects.filter(country='US'))
But, first of all that gives me the str back, which I don't really want (and changed it to be lot_id, but that's a hack.)
What's the best way to constrain Discrete through Lot and over to Lot_Country? In SQL I would just join in the subquery (or even in the main query - maybe that's what I need? I guess I don't know how to join up to a parent then down into that parent's other child...)
Thanks in advance for your help.
I'm not sure what you mean by "it gives me the str back"... Lot_Country.objects.filter(country='US') will return a queryset. Of course if you print it in your console, you will see a string.
I also think your models need refactoring. The way you have currently defined it, you can associate multiple Lot_Countrys with one Lot, and a country can only be associated with one lot.
If I understand your general model correctly that isn't what you want - you want to associate multiple Lots with one Lot_Country. To do that you need to reverse your foreign key relationship (i.e., put it inside the Lot).
Then, for fetching all the Discrete lots that are in a given country, you would do:
discretes_in_us = Discrete.objects.filter(lot__lot_country__country='US')
Which will give you a queryset of all Discretes whose Lot is in the US.
First. I am not sure how to write this question so it would be more useful to SO users, maybe someone could edit the question.
So, I have these two models:
class A(models.Model):
...
class B(models.Model):
foreign = models.Foreignkey(A)
aproperty = models.CharField(...)
And I have these two possibilities of a query in a view:
b_objs = B.objects.filter(aproperty=value)
a_objs = [b.foreign for b in b_objs]
or
a_objs = A.objects.filter(b__aproperty=value)
Are they equally expensive?
They are not only "not equally expensive", but the result is also different.
First approach: You query directly on B, then get all foreign by looping on b_objs. Result is a list.
Second approach: You did a database join operation in the underline implementation, then fetch the results. Result is a queryset.
Apparently the second approach is more efficient, because you only joined the database once to fetch the result, whereas the first approach you need to hit the database many times to get A objects.
Say I have a model that is
class Bottles(models.Model)
BottleCode = models.IntegerField()
class Labels(models.Model)
LabelCode = models.IntegerField()
How do I get a queryset of Bottles where the BottleCode and LabelCode are equal? (i.e. Bottles and Labels with no common Code are excluded)
It can be achieved via extra():
Bottles.objects.extra(where=["Bottles.BottleCode in (select Labels.LabelCode from Labels)"])
You may also need to add an app name prefix to the table names, e.g. app_bottles instead of bottles.
Though #danihp has a point here, if you would often encounter queries like these, when you are trying to relate unrelated models - you should probably think about changing your model design.
So first off, I want to clarify that I am trying to make One-To-Many relationships, not Many-to-One. I already understand how ForeignKeys work.
For the sake of the discussion, I've simplified my models; they're much more field-rich than this in the real implementation.
I have a model, called a ColumnDefinition:
class ColumnDefinition(Model):
column_name = CharField(max_length=32)
column_type = PositiveSmallIntegerField()
column_size = PositiveSmallIntegerField(null=True, blank=True)
I think have a registry. Each registry has a separate set of columns for it's input and output definition. I've put the theoretical "OneToManyField" in there to demonstrate what I'm trying to do.
class Registry(Model):
input_dictionary = OneToManyField(ColumnDefinition)
output_dictionary = OneToManyField(ColumnDefinition)
created_date = DateTimeField(auto_now_add=True, editable=False)
A ColumnDefinition is only ever related to one Registry ever. So it's not a Many to Many relationship. If I put a ForeignKey on the ColumnDefinition instead to create a reverse relationship, it can only create a single reverse, whereas I need both an input and output reverse.
I don't want to have to do anything kludgey like adding a "column_registry_type" field onto ColumnDefinition if I can get around it.
Does anyone have any good ideas on how to solve this problem?
Thanks!
You can add two ForeignKeys on ColumnDefinition, one for input and one for output, and give them separate related_names:
class ColumnDefinition(Model):
...
input_registry = models.ForeignKey(Registry, related_name='input_columns')
output_registry = models.ForeignKey(Registry, related_name='output_columns')
You can then access the set of columns like registry.input_columns.
You can and should define two different ForeignKey fields on ColumnDefinition. Just make sure to specify a related_name value for at least one of them.
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
I have something like this in models.py
class ZipCode(models.Model):
zip = models.CharField(max_length=20)
cities = City.objects.filter(zip=self).distinct()
class City(models.Model):
name = models.CharField(max_length=50)
slug = models.CharField(max_length=50)
state = models.ForeignKey(State)
zip = models.ManyToManyField(ZipCode)
When I do this I get:
NameError: name 'City' is not defined
Is this because the order of declaration matters? And if so, how can I do this, because either way I arrange this, it looks like I'm going to get a NameError.
Thanks.
Apart from order issues, this is wrong:
cities = City.objects.filter(zip=self).distinct()
It is not inside a method, so "self" will also be undefined. It is executed only once, at class-creation time (i.e. when the module is first imported), so the attribute created would be a class attribute and have the same value for all instances. What you might be looking for is this:
#property
def cities(self):
return City.objects.filter(zip=self).distinct()
Because this is inside a method, which is not executed until it's accessed, ordering issues will no longer be a problem. As ozan points out, this is a duplication of what Django reverse relations already give you for free:
a_zip_code.city_set.all()
And you can use related_name to call it what you like:
zip = models.ManyToManyField(ZipCode, related_name='cities')
...
a_zip_code.cities.all()
So I don't think the ordering issue you originally asked about is even relevant to your situation. When it is, others have already pointed out using quoted strings in ForeignKey and ManyToManyField declarations to get around it.
When you have references to classes defined after, you can use this trick:
attribute = models.ForeignKey('ClassDefinedAfterThis')
Yes order does matter as others have noted.
Though, encountering this issue is almost always going to be an indication that you're doing something wrong.
In this case your declaration:
cities = City.objects.filter(zip=self).distinct()
... is both redundant and bad practice. You can find the cities related to a zip code by referring to that zip code's city_set in your views (ie not in your model!). So if zip is an instance of ZipCode, you would do:
cities = zip.city_set.all()
If you really want to call it 'cities' rather than 'city_set' you can use the related_name parameter in your m2m declaration.
I was once worried about order... because I thought my models below could only reference models above. But then realized that you can just do a
models.ForeignKey('appName.modelName')
and all was fine.
Yes, order does matter, but your example does not look right to me. I think you should just be using a foreign key for your many-to-one relationship:
cities = models.ForeignKey(City)
This has the details on many-to-one relationships with django models.
Edit:
It was pointed out to me in the comments that cities in Europe might have several cities in the same zip code. If you are looking for a many-to-many relationship here, you should use:
cities = models.ManyToManyField(City)
This is described in Django's documentation. The point is, this is either of these examples are much more clear than what is used in the example.
Order matters in Python. This thread may be relevant to your question. Also for your uses, you may want to use a unique foreign key in the ZIP code class.