I have 2 models Foo and Bar. Each Foo has multiple Bars, but one needs to be the "default". At this point I have a foreignkey in Bar pointing to Foo, but now I need a way to specify which of the Bar's belonging to Foo is the default. I tried setting another foreignkey in Foo that points to a Bar (with a unique related_name), but I get all sorts of errors (including in the django-admin templates).
Code so far:
class Foo(models.Model):
default_bar = models.ForeignKey('Bar')
class Bar(models.Model):
foo = models.ForeignKey(Foo)
I have absolutely NO problem with a completely new solution as I'm probably doing it wrong anyways. The only other way I can think of is to have a separate table that connects Foos and Bars and having the Foo part unique, but that makes the admin interface a MESS.
If a Foo can have multiple Bars, then can a Bar belong to multiple Foos? If so then you can solve this really easily with a ManyToManyField and the through parameter, like this:
class Foo(models.Model):
name = models.CharField(max_length=128)
bars = models.ManyToManyField(Bar, through='FooBar')
class Bar(models.Model):
name = models.CharField(max_length=128)
class FooBar(models.Model):
foo = models.ForeignKey(Foo)
bar = models.ForeignKey(Bar)
is_default = models.BooleanField()
Check the Django documentation on "Extra fields on many-to-many relationships".
And just as a note, posting a simple version of your actual problem (ie. saying Person and Group vs. Foo and Bar) helps, as we get a better understanding of what you're actually trying to model! That way it's easier to see where the relationships should actually go.
Related
While refactoring an app, I've started removing a config file and replacing it with a new model, Foo in this example. Because of this, another model, Bar in this example, needs to change from having a CharField that was for the config file, to a ForeignKey that is for the new model.
Say I have
class Foo(models.Model):
name = models.CharField(primary_key=True, max_length=100)
class Bar(models.Model):
name_of_foo = models.CharField(max_length=100)
and I want to change name_of_foo to instead be a ForeignKey...
class Bar(models.Model):
foo = models.ForeignKey(Foo, on_delete=models.CASCADE, default=???)
I would like the default to be based on what name_of_foo was. If name_of_foo was "abc", I would like it to do something akin to default=Foo.objects.get("abc").
Is there a way to fill in the ??? such that this works nicely? If not, what steps can I take to arrive here, so that the existing data is converted?
I'm not sure if it is possible to achieve what you want using the default field but I'll leave it to someone else with more knowledge than me to answer that.
Assuming that a Foo object exists for each Bar object where foo.name = bar.name_of_foo you should be able to add a foreign key to Bar that links to the Foo object with the following steps.
Add foreign key field with null=True
class Bar(models.Model):
name_of_foo = models.CharField(max_length=100)
foo = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True)
run makemigrations and migrate commands
Open the django shell
python manage.py shell
run the following code to update the foreign key of each Bar object (making changes where necessary)
from <app_name>.models import Foo, Bar
for foo in Foo.objects.all():
bar = Bar.objects.get(name_of_foo=foo.name)
bar.foo = foo
bar.save()
remove name_of_foo field
class Bar(models.Model):
foo = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True)
run makemigrations and migrate commands
Lets say I have the following models:
class Foo(models.Model):
...
class Bar(models.Model):
name = models.CharField(max_length=255)
foo = models.ForeignKey(Foo)
Now, I am doing the following queries:
foo_pks = set([])
for bar in Bar.objects.filter(name='some_name'):
foo_pks.add(bar.foo.pk)
for foo in Foo.objects.filter(pk__in=foo_pks):
# Do something
So basically, I am adding the primary keys to the set, and then using that set to make another query. How many times am I hitting the database? Moreover, is this horribly inefficient, and if so, is there a better way to do this?
Let me explain. I have 2 tables which are child classes of another abstract table. The abstract table has a relationship to a model called Foo. The related_name is set dynamically. The code looks like this:
class Foo(models.Model):
...
class Parent(models.Model):
foo = models.ForeignKey(
Foo,
on_delete=models.CASCADE,
related_name='%(app_label)s_%(class)s_related'
)
...
def bar(self):
print('bar')
class Meta:
abstract = True
class ChildOne(Parent):
...
class ChildTwo(Parent):
...
Therefore, the related names become 'myapp_childone_related', and 'myapp_childtwo_related'.
Now, lets say I want to call the bar() method of all the objects from the ChildOne and ChildTwo model that is related to a Foo object. There is a catch though, I want to it from with a class method of the Foo model. Currently, I'm doing it like this:
class Foo(models.Model):
...
def call_bar(self):
references = ('childone', 'childtwo')
for ref in references:
children = getattr(self, f'myapp_{ref}_related').all()
for child in children:
child.bar()
This works fine, but honestly feels a bit hack-y, especially when dealing with more than two children classes. Is there a nicer, more Pythonic solution to this problem?
Edit: I decided not to mention previously that I wanted to call the bar() method from within a class method of the Foo model because I thought that it was unnecessary for this question. However, Daneil Roseman's answer suggested making a list of classes, which is a good solution, but it would not work within the class method, as the classes have not yet been defined at that point in the module. So mentioning that in this edit.
A related_name is only syntactic sugar for performing a query from the related class itself. So you should just do this explicitly:
child_classes = [ChildOne, ChildTwo]
for child_class in child_classes:
children = child_class.objects.filter(foo=foo)
This is a simplified version of my models:
class User(models.Model):
pass
class Foo(models.Model):
owners = models.ManyToManyField(User)
bar = models.ForeignKey(Bar)
class Bar(models.Mode)
pass
I have a user instance and I would like to compute a queryset for all Bar instances associated with that user. Going from user to Bar consists of getting all Foo objects that have user as owner and then getting all bar instances associated with each Foo.
How can I express this most efficiently using django queries?
Add related names to your model Foo. This will facilitate the writing of queries.
class Foo(models.Model):
owners = models.ManyToManyField('User', related_name='foo')
bar = models.ForeignKey('Bar', related_name='foo')
Now assuming that you have a single user instance as you mentioned, you could make a query like this:
user = User.objects.get(pk=1)
qs = Bar.objects.filter(foo__owners=user)
If you by most efficient mean the performance and not the expression, then you should take a look at prefetch_related and select_related methods of QuerySet.
I have two tables that are joined through a custom intermediary table:
class Foo(models.Model):
title = models.CharField(max_length=255)
class Bar(models.Model):
title = models.CharField(max_length=255)
foos = models.ManyToManyField(Foo, through="FooBar")
class FooBar(models.Model):
some_attr = models.BooleanField(default=True)
foo = models.ForeignKey(Foo)
bar = models.ForeignKey(Bar)
When it comes to testing the save functionality of these models, I'm at a bit of a loss. saving Foo and Bar instances on their own are fine, but how do I test that I can add and save many to many relationships with FooBar using mocking? Should the many to many addition test happen on the FooBar model or the Bar model? I guess I'm just looking for a bit of direction in testing these kinds of models using mocking rather than fixtures.
I ended up using factory-boy for this, it provides an easy way to set up instances for testing while being far more flexible than using fixtures.
Other options include model-mommy which does a similar thing, and people say the syntax is easier to work with.