I'm using multi-table-inheritance, and want to know how to create an inherited type from an instance of the superclass.
Using the example given in the documentation:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
Now when you create a Restaurant, you automatically make a Place, which is fine, and the behaviour I expect and want.
But what if I make a Place, and later decide I want to convert to a specific type (like Restaurant). How do you create a Restaurant, using an existing Place?
Multi-table inheritance is just OneToOneField relation between Place and Restaurant.
place = Place.objects.get(id=1)
# Create a restaurant using existing Place
restaurant = Resturant(place_ptr=place)
restaurant.save()
place = Place.objects.get(id=1)
# Create a restaurant using existing Place
place.__class__ = Restaurant
place.save()
restaurant = place
While undocumented, this seems to do the trick:
restaurant(place_ptr=place).save_base(raw=True)
This solves the problem without using any hacks and is the shortest solution, also in terms of processing, using Django APIs.
While searching for this solution, I also found a slightly longer one, but using documented APIs. It is basically the same as Mariusz answer, also see this answer for more details:
from django.forms.models import model_to_dict
restaurant(place_ptr=place, **model_to_dict(place)).save()
However, this second one is more risky due to limited field set returned by the model_to_dict (see again the answer explaining the differences among various methods presented). Naturally, it also generates more DB calls because it writes to both tables.
Related
Is it possible without related name (related_name="+") to prefetch objects on the target instance? Sure I know it's not a problem with the related name, but I'm not really sure if it's possible without it.
Here is the example code:
from django.db import models
class Parent(models.Model):
name = models.CharField(max_length=50)
class Child(models.Model):
parent = models.ForeignKey(to=Parent, related_name="+", on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Parent.objects.all().prefetch_related('child_set')
Maybe it's possible using the Prefetch(lookup, queryset=None, to_attr=None) object, because it takes the queryset in the argument list?
Looked through the code a bit and found this line:
rel_obj_descriptor = getattr(instance.__class__, through_attr, None)
Here instance is the model instance, and through_attr is the field name of related instance to be fetched. This line basically tries to get a related descriptor to perform the prefetch query. In your case rel_obj_descriptor would contain None.
To answer your question no it is not possible at least for a Foreign Key, there may be some hack for Many to Many relationships as Django appears to use some internal descriptors for them.
I would advice you to simply not set related_name="+" since you want to use the backwards relation here. You say "It's because of separation of concerns between multiple apps" but that does not make much sense. Don't we set a foreign key to the user model for various other models anyway and still use the related name? Does the point of separation of concerns arise there(the user model is in a separate app)?
try
parent = Parent.objects.get(id=pk)
parent.child_set.all()
I don't know if having related_name = '+' prevents this situation, but if you never define related_name, you can definitely use this method.
I have a couple of classes:
class Person(models.Model):
address = models.ForeignKey(Address, on_delete=models.CASCADE)
class Address(LiveModel):
address = models.CharField(max_length=512)
some_manager = SomeManager()
some_other_object_manager = OtherManager()
class Meta:
base_manager_name = 'some_other_object_manager'
Because I set some_manager, the default manager used is SomeManager which is good. BUT if I am querying a Person, I want it to use Address's OtherManager manager for querying and I thought that by setting base_manager_name, I would be able to achieve this (https://docs.djangoproject.com/en/2.2/topics/db/managers/#using-managers-for-related-object-access). Unfortunately this does not work. Any ideas? Particularly I am trying to achieve this in the admin, if that makes a difference.
To clarify, this does work as intended. The issue here was in the detail. I was using the Django admin which does not query the related fields the way I expected. It actually uses the related fields default manager for the queryset. If you want to do what I am trying to do, this is a nice simple example: https://books.agiliq.com/projects/django-admin-cookbook/en/latest/filter_fk_dropdown.html
What I want is to retrieve all the fields belonging to a Model of a foreign key.
My models for example:
class BaseProduct(models.Model):
name = models.CharField(max_length=256)
variant = models.CharField(max_length=256, default='N/A')
type = models.ForeignKey(ProductType)
class ProductType(models.Model):
name = models.CharField(max_length=256,blank=False,null=False)
sofa = models.ForeignKey(SofaProduct, blank=True, null=True)
toaster = models.ForeignKey(ToasterProduct, blank=True, null=True)
These are just examples, there can be any number of ProductType models each with any number of fields.
In my template I can display all the fields of the BaseProduct by using the BaseProduct ID. What I want is to display all the fields of the FK.
For example if type = sofa in BaseProduct, I need to retrieve and display all sofa fields as well as BaseProduct fields.
(disclaimer: I have a tendency to give really long answers. You'll have to forgive me for that)
First rule of schema design - It should reflect your real world business logic (not the actual business action mind you, just the implications of the relationships). For example, if I have a class Person I can create a class Pet with a foreginKey to Person which translates to - every person can have multiple pets.
If we apply that logic to your schema we see that ProductType is a class that has a foreignKey to both Sofas and Toasters, which means each Toaster can have multiple Sofas and vice versa. Last time I checked, I never heard of a Sofa that had a Toaster.
In other words - you need to think what you're actually trying to achieve here. I'm guessing BaseProduct is a basic class that has common fields, and Sofa and Toaster are different types of products. Since they are different, they have their own special fields, and shouldn't be related, so it makes sense to have them as separate models. So why do you even need ProductType? To define the name Toaster? You're already defining an entire model! Why do you need to keep its name on a different table (and not, say, some custom method that always returns "I am a toaster, hear me roar")?
My best guess is that you want to be able to define new types of products on the go. However, if you intend to keep them separated on the model level, then you'll have to create a model for each new product. And if you want to be able to simple define a new model with ProductType, then you either need to have one Product class to manage them all, or you want a complicated dynamic system that can create new models on the fly.
Let's break those options down:
Create a generic product and a type class, like you did there:
class ProductType(models.Model):
name = models.CharField(max_length=256,blank=False,null=False)
class Product(models.Model):
name = models.CharField(max_length=256)
variant = models.CharField(max_length=256, default='N/A')
type = models.ForeignKey(ProductType)
Now each product can only be of one type, and you can always create new types on the go. This of course means all Product objects will share the same fields, and is very limiting. You won't have the same flexibility for each type like you would before (no sofa-only fields), but on the other hand it will be easier to create dynamic types of objects - you just define a new ProductType and bam you have a whole new group of products.
Create a basic abstract Product model, and define a new sub-model for each new type of product. You'll have a lot more flexibility for each one, but defining new types will always require defining a new model and setting up a table for it. With this scheme you don't need the ProductType object at all because the different models define the different types (there's no need for duplicity).
You can create some kind of admin page for the process, but it's not going
to be very easy to setup, and you might find yourself eventually with too many tables
(which can be especially problematic if you need to sometimes query
on all products - you'll have to join a lot of different tables,
which is not very efficient).
Use a non-relational database with some dynamic-models know how and disco*
*ok, it's actually more complicated than that, but the explanation on how to combine them is way too long, even for my answer. If it seems over your head, forget about it. If you have some idea about how non-relation databases work, you can probably figure it out yourself
Your question is somewhat unclear.
I think you want Django modal forms to display all fields of an modal.
def ListForm(Forms.form):
model = MyModel
fields='__all__' #Sets display all
fk_name ="Model_to_use" #Is needed when your model has more then one fk
Django model form
You can use _set for accessing related objects. For example, if you have two models like these:
class MyModel(models.Model):
name = models.CharField(max_length=200)
somedata = models.CharField(max_length=200)
class AnotherModel(models.Model):
name = models.CharField(max_length=256,blank=False,null=False)
referral = models.ForeignKey(MyModel)
type = models.CharField(max_length=256,blank=False,null=False)
you can access the name field of AnotherModel with
>>> m = MyModel.objects.get(id=1)
>>> m.AnotherModel_set.all()[0].name
See: https://docs.djangoproject.com/en/dev/topics/db/queries/#related-objects
On a side note, you should probably rethink your models structure, as yuvi pointed out.
Wih the following model setup:
class Cat(models.Model):
claw = models.CharField(max_length=20)
name = models.CharField(max_length=20)
class Fur(models.Model):
type = models.CharField(max_length=20)
cat = models.ForeignKey(Cat)
class Meta:
db_table=u'cat_view'
managed=False
Fur has a foreign key to Cat. CatView is a subset view of Cat that is being managed manually. Is there a way to make use of django's useful reverse set methods with this setup?
Additionally, I could just use Fur.objects.filter(cat_id=cat_view.id, ...) which would be the same functionality as cat_view.fur_set.filter(...), however I could not do reverse lookups such as CatView.objects.filter(fur__type="shaggy").
EDIT:
Added example models file, changed image for clarity, added minor complexity to question.
Firstly, neither of those ORM calls you give will work: fur_set is an attribute of a cat instance, not of the Cat.objects Manager.
Secondly, Django has no specific support for database views at all, so your question about using this particular bit of functionality with a view is a bit strange. You could define CatView as a separate (unmanaged) model, although you'd need to be careful about updating and saving. Then you can get the same effect as the reverse relation by querying the Fur object directly:
Fur.objects.filter(cat=my_cat_view.id)
I'm finding conflicting information on whether to use OneToOneField(User) or ForeignKey(User, unique=True) when creating a UserProfile model by extending the Django User model.
Is it better to use this?:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
or this?:
class UserProfile(models.Model):
user = models.OneToOneField(User)
The Django Doc specifies OneToOneField, while the Django Book example uses ForeignKey.
James Bennett also has two Blog posts that providing conflicting examples as well:
Extending the User Model
User Registration
In the former post, Bennett provides some reasons why he switched to using ForeignKey instead of OneToOneField, but I don't quite get it, especially when I see other posts that recommend the opposite.
I'm curious to know your preference and why. Or, does it even matter?
The only real reason given in the article is that it can be set up so that the admin page for User will show both the fields in User and UserProfile. This can be replicated with a OneToOneField with a little elbow grease, so unless you're addicted to showing it in the admin page with no work at the cost of a bit of clarity ("We can create multiple profiles per user?! Oh no, wait, it's set unique.") I'd use OneToOneField.
Besides the admin page inlines, other reason for the ForeignKey solution is that it allows you to use the correct, default DB manager when objects are accessed with a reverse relation. Consider example from this subclasses manager snippet. Let's say that the Post class definition from the example looks like this:
class Post(ParentModel):
title = models.CharField(max_length=50)
onetoone = models.ForeignKey(SomeModel, unique=True)
children = ChildManager()
objects = models.Manager()
By calling somemodel_instance.post_set.all()[0], you get the desired subclasses objects of the Post class as indicated by defining the first (default) manager as a ChildManager. On the other hand, with OneToOneField, by calling somemodel_instance.post you get the Post class instance. You can always call somemodel_instance.post.subclass_object and get the same result, but the default manager could do any other sort of tricks and the FK solutions hides them nicely.
If you own and can modify the custom manager code you can use the use_for_related_fields attribute instead of using FK in place of legitimate 1to1 field, but even that can fail because of some not-known to me nuisances of the automatic managers. As far as I remember it will fail in the above example.
Other reason to generally not use the OneToOneField related to reverse relations: when you use reverse relations defined via OneToOneField you get an model instance, contrary to Manager for ForeignKey reverse relation and as a consequence there's always a DB hit. This is costly if you do some generic stuff on reverse relations (via _meta.get_all_related_objects()) and do not know and care if you will use them all or not.