I am quite new to Django and I am creating an app which stores unit conversion tables for an application on a mobile device.
Each UnitCategory can have multiple Units, each of which have a multiplication factor to use to convert values between them. The current model could be simplified to this:
class UnitCategory(models.Model):
name=models.CharField(max_length=100)
class Unit(models.Model):
name=models.CharField(max_length=200)
multiplicand=models.FloatField(default=1)
category=models.ForeignKey('UnitCategory',related_name='units_set')
With admin classes:
class UnitCategoryAdmin(admin.ModelAdmin):
model=UnitCategory
inlines=[UnitInline]
class UnitInline(admin.TabularInline):
model=Unit
Now, rather than entering all of the multiplication factors for all potential units, most of the units in many categories can be built as a function of other base categories, e.g a simple case would be: [Speed] = [Distance] x [Time]^-1.
I am trying to add a class to provide another set of inlines to store data for compound conversion. Each inline would have a dropdown list to select a base unit category and an integer field to enter power it is raised to. I know how to create the dropdown list using a ForeignKey, but I am already using one ForeignKey to link the inline model to the UnitCategory, so I end up with two ForeignKeys pointing to the same parent class:
class CompoundConversionCategory(models.Model):
parent=models.ForeignKey('UnitCategory',related_name='compounds_set')
category=models.ForeignKey('UnitCategory',related_name='category_set')
power=models.IntegerField(default=1)
where the admin classes become:
class UnitCategoryAdmin(admin.ModelAdmin):
model=UnitCategory
inlines=[UnitInline,CompoundCategoryInline]
class UnitInline(admin.TabularInline):
model=Unit
class CompoundCategoryInline(admin.TabularInline):
class=CompoundConversionCategory
Not surprisingly, Django doesn't like me using two ForeignKeys to the same parent class. Is there a way to specify that one of them should be linked to a different object than the parent, or some other more appropriate way to to create a dropdown list from the model?
I believe you are looking for admin.InlineModelAdmin.fk_name. (https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.fk_name)
Related
For example, I have the following models:
class Transport(models.Model):
pass
class Car(models.Model):
type = models.ForeignKey(Transport, related_name='cars')
class Train(models.Model):
type = models.ForeignKey(Transport, related_name='trains')
To fetch cars and trains separately I can do the following:
Transport.objects.first().cars.all()
Transport.objects.first().trains.all()
But how can I do it in one query?
You can prefetch the related cars and trains:
vehicle = Transport.objects.first().prefetch_related('cars', 'trains')
Then you can access the related sets:
print(vehicle.cars_set.all())
print(vehicle.trains_set.all())
The main gotcha is that you may only access the related set via .all() (ie. no filtering, no .first() etc.), anything else would trigger a new query, leaving you worse off in terms of performance than without the prefetch.
For example:
class Contact(models.Model):
contacts = models.ManyToManyField('self', through='ContactRelationship', symmetrical=False)
What does the symmetrical=False parameter do?
When should it be left as True, and when should it be set as False?
How does this settings affect the database (does it create extra columns etc)?
Let's say you have two instances of Contact, John and Judy. You may decide to make John a contact of Judy. Should this action also make Judy a contact of John? If so, symmetrical=True. If not, symmetrical=False
Here is what is says in the documentation:
Only used in the definition of ManyToManyFields on self. Consider the following model:
from django.db import models
class Person(models.Model):
friends = models.ManyToManyField("self")
When Django processes this model, it identifies that it has a ManyToManyField on itself, and as a result, it doesn’t add a person_set attribute to the Person class. Instead, the ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.
By default, the value of symmetrical is True for Many to Many Field which is a bi-directional relationship.
Using a through table (symmetrical=False):
But you can also imagine a situation where you don't need this type of relationship so you can add symmetrical=False. And, this can be achieved by using a through table because by default symmetrical is False if you use a through table:
Recursive relationships using an intermediary model are always defined as non-symmetrical – that is, with symmetrical=False – therefore, there is the concept of a “source” and a “target”. In that case 'field1' will be treated as the “source” of the relationship and 'field2' as the “target”.
So you can imagine a situation where you do need the direction i.e. let's say there is a Node model and it has a relationship with itself using a through table. If we didn't have the requirement of direction here we could go with the example shown earlier. But now we also need a direction from one node to another where one being source and another one being target and due to nature of this relationship it cannot be symmetrical.
I am designing a contact relationship application that needs to store contacts in groups. Basically I have 7 "group types" (simplified it to 3 for my image), each group type shares the same fields so I thought that it would make sense to use an abstract "group", and let all group types inherit the methods from this abstract group.
So this is basically the idea:
However, this approach results in a couple of unexpected difficulties. For example:
I am not able to use a foreignkey of an abstract class, so if I would want to model a relationship between a group and a contact, I have to use the following approach:
limit = (models.Q(app_label='groups', model="Group type A") |
models.Q(app_label='groups', model="Group type B") |
models.Q(app_label='groups', model="Group type C")
)
group_type = models.ForeignKey(ContentType, limit_choices_to=limit)
group_id = models.PositiveIntegerField()
group = GenericForeignKey('group_type', 'group_id')
This seems quite hacky, and with this approach I am forced to do some hard coding as well. I am not able to call all groups with a simple query, maybe a new group will be added in the future.
Is there a better approach to model a relationship like this? Am I using the abstract class completely wrong?
Edit: some extra explanation in response to the questions.
A user is connected to a group with another object called "WorkRelation", because there is some extra data that is relevant when assigning a user to a group (for example his function).
I initially went for an abstract class because I thought that this would give me the flexibility to get all Group types be just calling Group.objects.all(). If I would use a base model, the groups aren't connected and I will also have to hard-code all group names.
Since your child models do not have additional fields, you can make them proxy models of the base group model. Proxy models do not create new database tables, they just allow having different programmatic interfaces over the same table.
You could then define your ForeignKey to the base group model:
group = ForeignKey(BaseGroup)
Use django-polymodels or a similar app to have the groups casted to the right type when queried.
More on model inheritance in the doc.
Why don't use solid base model instead of abstract model? Then you just put contacts as either ForeignKey or ManyToMany to the base model.
I have a scenario where a user need to enter a type of contribution. It can be cash or material. Based on his contribution type, I need to store the cash in IntegerField or material in CharField. How can I do it without making two fields in the model and leaving one always as empty field.
class Contribution(models.Model):
CONTRIBUTION_TYPE_CASH = "cash"
CONTRIBUTION_TYPE_MATERIAL = "material"
CONTRIBUTION_TYPE_CHOICES = [
(CONTRIBUTION_TYPE_CASH, _("cash")),
(CONTRIBUTION_TYPE_MATERIAL, _("material"))
]
contributor = models.ForeignKey(Contributor, related_name="donor", verbose_name=_("contributor"))
type = models.CharField(max_length=20, choices=CONTRIBUTION_TYPE_CHOICES, verbose_name=_("contribution type"))
First variant, keep a single CharField and make sure you properly validate input depending on type. You will have to deal with strings all the time, even if the actual value is a number.
Second variant, use model inheritance and define two different models, one for material contributions and another for cash contributions. You can use an abstract parent in which case you'd have to manually merge the two querysets for getting a global contribution list. Or you could use a concrete parent and use a third party package such as django_polymorphic to seamlessly deal with inherited instances. Either way you'd have to create the apropriate model instance in your backend, even if you use the same dynamic form in your frontend.
The Django docs recommend copying a model instance thus:
original.pk = None
original.save()
But if you "use inheritance" -- apparently meaning if the model's class is a subclass of a subclass of models.Model -- you need to do it slightly differently.
Specifically, the doc says:
Due to how inheritance works, you have to set both pk and id to None:
and gives an example analogous to this:
original.pk = None
original.id = None
original.save()
This seems kludgey. In any case, I'd like to understand what's going on. Why does using inheritance require you to set the id field to None also? Don't all Django models inherit from models.Model in any case?
(NOTE: I'm omitting the bit from the doc about copying m2m fields, which incidentally seems even more kludgey.)
It's because MTI (Multiple Table Inheritance), the type you're talking about here, stores the object across multiple tables. Take this example:
class Animal(models.Model):
...
class Dog(Animal):
...
When you create a Dog, all the fields on Animal are saved into the table for Animal, and just the fields directly on Dog are saved to the table for Dog. When you lookup the Dog later, Django queries both tables and stitches them together.
Both tables, however need primary keys, and Django uses AutoFields for that, which are simply positive integer fields. So Dog has an id and Animal has an id. The pk is filled with the id for the Animal part because this is the main piece, and Dog's id doesn't matter. However, if you're going to make a copy, you need to copy both pieces. Otherwise, the Animal part will of the copy will not get it's own Dog part of the copy.