I'm having a bit of a problem trying to model something in Django that I've conceptualized. I know that it is a many to many relationship... however it is sort of self referential and has a quantity involved. I imagine this requires a bridge model of some sort, which I have, but now how do I edit them in the admin page?
What I have is a Component class. For example, a 'screw' is a component, and it requires no further components to create it. But so is a 'housing', which requires 4 screws to hold it together. That housing could then go into a manifold and the manifold could go into a vehicle and so forth.
Each thing could potentially be a component of another thing if that makes sense. I've put all of the screws and bolts and such into the database through the admin edit page. But now I want to start putting in more complex assemblies. I could just create an Assembly class which has a list of one or more components. But I'm still left with the problem that this assembly could go into a larger assembly zero or more times.
How do I represent that?
currently I have
class ComponentBase(models.Model)
name = models.CharField(max_length=255)
class Meta:
abstract = True
ordering = ['name']
class ItemComponent(ComponentBase):
components = models.ManyToManyField('ItemComponentWithQuantity', blank=True)
class ItemComponentWithQuantity(ItemComponent):
quantity = models.IntegerField(default=1)
assuming this is the correct way to model this (is it?) how do I get the admin edit form to set this up a bit like a spreadsheet or list?
like
name: manifold assembly
components:
10x screws
10x bolts
1x assembly housing
The components field should only show the ones that have been added and the quantity. Not all possible components.
i had my model originally set up to have components = models.ManyToManyField('ItemComponent', blank=True). This caused the admin panel to have a list of all the existing ItemComponents as expected, but obviously no quantities.
Adding the ItemComponentWithQuantity class in, I changed the manytomanyfield to ItemComponentWithQuantity. But now the admin components field is empty.
I hope I'm making sense here. I'm not sure what I'm doing incorrectly.
thanks in advance.
EK
The inheritance that you're doing is making this too complicated. You can model this a different way. Change your ComponentBase to a basic model.Model (not abstract) named Assembly. Change ItemComponent to just Component. Lastly change your ItemComponentWithQuantity to AssemblyComponent.
The AssemblyComponent should have the fields
assembly - ForeignKey to Assembly
component - ForeignKey to Component
quantity - IntegerField
Make another model AssemblyAssembly with the fields
assembly_parent - ForeignKey to Assembly
assembly_child - ForeignKey to Assembly
quantity - IntegerField
You would then create either a Tabular or Stacked inline admin (depends on desired user experience) for both AssemblyComponent and AssemblyAssembly. Be sure to set fk_name in the AssemblyAssembly to point to the assembly_parent as the fk_name because you have two foreign key fields referencing the same model. You can use a raw_id_field for the assembly_child if there will be a lot of possible assemblies in the system. Use the inline admins for the Assembly admin.
For more about inline admins, see: https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#inlinemodeladmin-objects
That will give you roughly the admin UI that you want with the exception that component and assembly links will be in separate inline admins.
For this you can just have a single model and make it a recursive model where it is in a relationship with itself
component = models.ForeignKey('self', on_delete=models.CASCADE)
i also advice you add an extra field for quantity instead of creating a whole model for that. Hope this works.
Related
Lets say I have a recipe website with two basic models, 'User' and 'Recipe'
class User(models.Model):
username= models.CharField()
email = models.CharField()
class Recipe(models.Model):
name = models.CharField()
description = models.CharField()
I would like to add the functionality so that users can 'favorite' a recipe.
In this case, I need to use a many-to-many relationship. My question is, how do I decide which model to add the relationship to?
For example, each user could have a list of 'favorite' recipes:
class User(models.Model):
favorites = models.ManyToManyField(Recipe)
Alternatively, each recipe could have a list of users who Favorited the recipe:
class Recipe(models.Model):
user_favorites = models.ManyToManyField(User)
What is considered the best practice? Is either one better for query performance?
It makes no difference from the database point of view, as pointed out in the comments.
But I have had two arguments where it did matter to me.
First (maybe less important), the built-in admin treats the two models differently by default. The model on which you define the relationship gets a widget for choosing the related objects. And a '+' for conveniently adding new objects of the related type.
secondly, you have to import one of the models in the file of the other one, if they are in different files. This matters if you want to write a reusable app that does not depend on anything outside. It mattered to me also because:
I once (well, not just once actually :)) broke my app/database/etc such, that I decided to start a new project and copy the code there. In this case you have to comment out some settings.INSTALLED_APPS to test step for step that everything works. Here it is important not to have circular includes (to include a commented-out app raises an error). So I try to import the "most basic" into the others, and not the other way round.
This not a simple answer to your question, but two points which I consider. Maybe some more experienced users can correct me if it's wrong in some sense.
First my django knowledge is beginner level, so please be patient with me.
I am faced with a model relationship that I do not know how to handle. I have 3 models: Project, Location and SubLocation.
A project can have multiple locations and each location can have many sublocations. I have a many to many field for location in the Project model and a foreignkey field for location in the Sublocation model.
class Project(models.Model):
...
locations = models.ManyToManyField(Location)
class Location(models.Model):
name = models.CharField(max_length=250, unique=True)
class SubLocation(models.Model):
location = models.ForeignKey(Location)
name = models.CharField(max_length=100, unique=True)
In django admin, I am able to add multiple locations when creating a project(Using filter_horizontal). However, I also need the option to select sublocations based on an added location for the project being created. I did not know how to do it with the above approach.
I then removed the locations many to many field from the project model tried the approach below.
I created a ProjectLocation model and added it as an inline to the Project ModelAdmin to be able to add locations and sublocations when creating a project. The model that looks as follows:
class ProjectLocation(models.Model):
project = models.ForeignKey(Project)
location = models.ManyToManyField(Location)
sublocations = models.ManyToManyField(SubLocation)
However, the approach does not work as desired since you can add any sublocations irregardless of the locations added. What I would like is to be able to add locations and their relevant sublocations when creating a project.
I read through generic relations as another possible approach but still I did not know how to pull it off.
With my model structure, is that possible?
If True, what should I do to get the desired result?
If False, please recommend how I could change it.
I think if you use foreign key it will be easier for your case, and it will easy to use with the _set option from django.
Yes it is possible but it will not be dynamic (meaning that changing the value of location will not magically update themselves without first saving the change) which might be very unpractical.
You should use formfield_for_manytomany in your admin -> https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_manytomany
The easiest way to implement this would be to add custom javascript filtering on that particular admin form
Leave for models as it was and try to use inlines in admin page.
So, your admins.py would look something like this:
class SubLocationInline(admin.StackedInline):
model = SubLocation
#admin.register(Location)
class LocationAdmin(admin.ModelAdmin):
.....
inlines = [SubLocationInline]
I'm developing kind of a social network in Django, but I don't see the way to maintain coupled my models.
I have a model User, which can comment on other Users, Pics, Pages (capitals are because they're models) via a Comment model. In order to arrange all that comments I created the Board model, that has one owner and receives all the comments addresed to that owner. The problem is I don't know how is its owner, i.e., which model does it belong to. It could be a pic's board, but also a user's one. So I end up with a model like this:
class Board(models.Model):
category = models.IntegerField()
owner_id = models.IntegerField()
I store owner's id and owner's category to be able to perform other tasks, but this solution doesn't convince me at all. A friend has advised me to use an abstract class Board, and then create a UserBoard, PicBoard, PageBoard which inherit from Board, but I think that will be useless too. I've thought about GenericRelations, but I'm not sure if that is the correct way to achieve what I'm trying. Any proposal will be listened and considered, thanks for your help :)
PS: Oh, and sorry if the title isn't very descriptive, but I couldn't figure out a better one. It's hard to explain this problem
Option 1
Create a class BoardOwner. Have all models which have a board inherit from that, and have the board have a foreignkey relationship with BoardOwner. You'll need to then scan over the various childlinks to figure out which one is the "real" child. This more fiddly, but localises all of the complexity in one class.
Option 2
Have a foreignkey from each class that has a board to the board. If you always have boards enter your system via a method on the owner (as opposed to a query on Board or elsewhere), you can have code in the owner which lets the Board know which foreign key reverse relationship to use. You'll probably want to factor that code into its own class.
Option 3
Use contenttypes: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
This wraps up this sort of thing.
I'm building my first Django app to manage multiple SaaS products.
This entails storing custom attributes for each Version of each Product.
For example, a new version of a Product is released that includes new configuration options that the earlier versions of the Product do not support.
I need to be able to keep track of those new values for each instance of the new Version.
I'm thinking I want the Admins to be able to add "custom fields" at the Product level by Version.
Looking for suggestions as to the best approach.
Thanks.
The common way of tracking model versions is to use django-reversion.
It sounds like each instance needs its own custom attributes. That means that changing the Models relating to Product and Version need not occur. This is good, because models can only change with the code (unless you get into dynamically generating Models which is usually not a good idea).
So, you need to be able to model attributes for each Product instance, regardless of Version. This should be a simple data modelling exercise, not necessarily related to Django.
A Product has a set of fields
A Product has a Version
A Product has a set of Attributes
This is quite easily modelled, depending on how you want to manage attributes.
class Version(models.Model):
version = models.CharField(max_length=10)
class ProductAttributes(models.Model):
name = models.CharField(max_length=64)
description = models.CharField(max_length=255)
# other fields as necessary
class Product(models.Model):
name = models.CharField(max_length=64)
version = models.ForeignKey(Version)
attributes = models.ManyToManyField(ProductAttributes, related_name='products')
That should be your modelling sorted in a very basic way. Now, let's create some instances.
v1 = Version(version='1.0.0')
v1.save()
hosted = ProductAttributes(name='Hosted', description='We host the apps!')
hosted.save()
shiny = ProductAttributes(name='Shiny', description='I like shiny')
shiny.save()
p = Product(name='Web Based Email', version=v1)
p.save()
p.attributes.add(hosted)
p.attributes.add(shiny)
p.attributes.all()
# shows shiny and hosted!
You can tweak the ModelAdmin for Product such that you can add ProductAttributes inline when adding or editing a Product. You can also have a separate ModelAdmin for ProductAttributes so you can create a list of known Attributes that can be applied to products at a later date.
There are two basic approaches for this.
Use a document based db (ie, "NoSQL") like Couch or Mongo. These have flexible schemas, so allow for multiple variations on a product.
Use the Entity Attribute Value (wikipedia) schema pattern. django-eav is an app that provides this.
Decide to go with sub-classes with each Product as each has a limited set of specific attributes that won't change much or at all over time. Thanks for all the great feedback. Learned a lot :-)
I've got a hierarchical model structure and have defined by own field types using OneToOneFields as described here.
I'm now writing a view customiser which takes any Model within this hierarchy and creates a form on-the-fly with checkboxes for each field to toggle visibility. Now I would like to remove the fields that link the models together (i.e. all those described using parent_link=True) but the property seems to have vanished from the fields in question, much to my displeasure. (I'm doing this because no-one would wish to view a Parent Relationship in the views I have composed).
My question is therefore, does anyone know how to locate that property at runtime?