Is there a way in Django to have multiple objects stored and manageable (in Django admin) inside another object?
Example, I have two models: Items and RMA. The RMA may have multiple Items inside of it. Each Item is unique in the sense that it is an inventoried part, so I can't just reference the same item multiple times with foreignKey (though maybe I'm misunderstanding its use/implementation).
So for now, I have an Item model:
class Item(models.Model):
serial_number = models.CharField(max_length=200)
name = models.CharField(max_length=200)
part_number = models.CharField(max_length=200)
location = models.CharField(max_length=200)
def __unicode__(self):
return self.name
And an RMA model:
class RMA(models.Model):
number = models.AutoField(primary_key=True)
items = ?????
Ultimately I'd like to be able to maintain use of the Django admin functionality to add/remove items from an RMA if necessary, so I've been staying away from serializing a list and then deserializing on display. Any help would be much appreciated.
You're modeling a has-many relationship.
This would be modeled with a Foreign Key on Item to RMA:
class Item(models.Model):
serial_number = models.CharField(max_length=200)
name = models.CharField(max_length=200)
part_number = models.CharField(max_length=200)
location = models.CharField(max_length=200)
rma = models.ForeignKey(RMA)
def __unicode__(self):
return self.name
To make it accessible in the admin of RMA you need djangos InlineAdmin functionality.
You can find examples in the django tutorial part2.
You are effectively describing a Many-To-One relation and to do this you are going to have to add the ForeignKey reference to the Item model, not to the RMA model.
You can also add a related_name to give the RMA model an attribute that you can call.
For example:
class Item(models.Model):
rma = models.ForeignKey(RMA,related_name="items")
serial_number = models.CharField(max_length=200)
# etc...
To manage the creation of these, you'll need an InlineModelAdmin form, so your admin.py file will need to look like this:
from django.contrib import admin
class ItemInline(admin.TabularInline):
model = Item
class RMAAdmin(admin.ModelAdmin):
inlines = [
ItemInline,
]
Related
I am trying to query a model against a field on a ForeignKey object's property.
I have the following models:
class Song(models.Model):
name = models.CharField(max_length=20)
limit = models.IntegerField()
class Recording(models.Model):
song = models.ForeignKey(Song, on_delete=models.CASCADE)
status = models.CharField(
max_length=1,
choices=STATUS_CHOICES,
default=OPEN
)
I would like to query Songs that have Recordings with status OPEN with a count of more than 'limit' (the field on Song). Looking over the django aggregation docs I tried something along the lines of:
# View
get(self):
songs_count = Count('recording', filter=Q(recording__status='O'))
songs = Song.objects.annotate(songs_count=songs_count)
results = songs.filter(songs_count__gt=< each song.limit... >)
Can someone point the way on how to build this query?
I greatly appreciate any and all feedback.
You can work with an F object [Django-doc] to refer to a field, so:
from django.db.models import F, Q
Songs.objects.annotate(
songs_count=Count('recording', filter=Q(recording__status='O'))
).filter(songs_count__gt=F('limit'))
I am developing a simple restaurant app in django and mysql and after days of googling and research, i wasn't able to find a suitable answere for this particular problem, here is my django Model
class MenuItem(models.Model):
menu_category = models.ForeignKey(MenuCategory, to_field='slug', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
image = models.ImageField(upload_to='uploads/menu/')
slug = models.SlugField(null=True, blank=True, unique=True)
price = models.DecimalField(max_digits=9, decimal_places=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
and here is the problem:
for example i have a menuItem "MEXICAN BURGER" i would like to ask the user their choice of meat.. ie either chicken or beef, or their choice of bread, white or brown,
or i might be having a MenuItem "OMELETTE COMBO" and i would like to ask the user the type of omellete they'd like to have eg ('spanish omelete', 'spinach and mushroom omelete')
or i might be having a MenuItem "ESPRESSO" and i would like them to choose between ('single', 'double')
*one menuitem can have multitple choices related to it, ie burger item can have choice of bread as well as choice of meat
for a better understanding of my problem visit this
link
*
and this another link
You can create another model as needed e.g. MeatOption and add to it a ForeignKey to MenuItem:
class MeatOption(models.Model):
meat_type = models.CharField(max_length=200)
class MenuItem(models.Model):
meat_type = models.ForeignKey(MeatOption, null=True, blank=True)
# rest of fields
Ideally you should think of categorizing your items and reflect that categorization into your models. i.e. you could have class Burgers, class Beverages and so on, so that you only include the relevant options.
And if there is common functionality among all those categories you could inherit that from a Base MenuItem class like so:
class MenuItem(models.Model):
# common fields across all items go here
class Burger(MenuItem):
# burger-specific fields here
meat_type = models.ForeignKey(MeatOption)
class Omelette(MenuItem):
# omelette-specific fields here
ingredient = models.ManyToManyField(Ingredient)
class Ingredient(models.Model):
name = models.CharField(max_length=200)
the details depend on your use-case of course
There are different solutions to this.
Code Based: Inheritance
You create Mixins for each of your choice type and subclass MenuItem and these mixins.
from django.db.models import CharField, TextChoices, Model
class MeatChoices(TextChoices):
CHICKEN = 'chi'
BEEF = 'bee'
class MeatMixin(Model):
meat_choices = CharField(max_length=3, choices=MeatChoices.choices, default=MeatChoices.CHICKEN)
class MeatMenuItem(MeatMixin, MenuItem):
pass
This will require code changes whenever more choices are added to the menu or the menu is changed in that matter.
Data Based: Generic Models
To allow the admin user to create and add choices to the menu items via the Django Admin, you need to create a model structure that enables specifying choices:
from django.contrib.postgres.fields import ArrayField
from django.db.models import ManyToManyField, CharField, Model
class Ingredient(Model):
name = CharField(max_length=50, unique=True)
class IngredientChoiceGroup(Model):
name = CharField(max_length=50, unique=True)
# simple solution for postgres only:
# ingredients = ArrayField(CharField(max_length=50))
# if not postgres or you need more attributes to ingredient:
ingredients = ManyToManyField(Ingredient)
class MenuItem(models.Model):
menu_category = models.ForeignKey(MenuCategory, to_field='slug', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
image = models.ImageField(upload_to='uploads/menu/')
slug = models.SlugField(null=True, blank=True, unique=True)
price = models.DecimalField(max_digits=9, decimal_places=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
ingredient_choices = ManyToManyField(IngredientChoiceGroup)
def __str__(self):
return self.name
This method would allow to configure multiple choice groups per menu item, e.g. "BREAD" and "MEAT". And you can do that all via the Django Admin.
You might need to work out the details. This is just a draft.
Note about ArrayField: it is a simpler structure in the DB, but for the Admin you will need to add a specific widget via 3rd party library or at least write your own parser. In the end, M2M field might be the better choice.
I have a model with an attribute that is connected to another model as follow:
class Book(models.Model):
synced = models.OneToOneField('SyncedBook'
related_name='internal',
on_delete=models.CASCADE)
# some more attributes here...
#property
def book_address(self)
return self.synced.book_address
However, the book_address is a also a FK in the SyncedBook table as follow:
book_address = models.ForeignKey('Address', db_index=True, null=True, blank=True,
related_name='address_book', on_delete=models.PROTECT)
I don't know and understand how to be able to edit the book_address through the Django admin page in class BookingAdmin(admin.ModelAdmin), even though I have read over the documentation. At first I have the attribute as readonly, but now I want to be able to edit it and save the new address from the Address table. Is there a way to make it happen through the class BookingAdmin(admin.ModelAdmin) and how? Any example and solution would be appreciate
Model properties are typically used for presenting logically defined data for a particular model instance and not necessarily storing data on the model instance itself.
An example of when to use a model property is as follows:
# Defines a product instance
class Product(model.Models):
name = models.CharField(max_length=100)
description = models.TextField()
active = models.BooleanField(default=True)
cost = models.DecimalField(max_digits=5, decimal_places=2)
price = models.DecimalField(max_digits=5, decimal_places=2)
# calculate profits on product
#property
def profit(self)
p = self.price - self.cost
return p
In your case, you are trying to actually be able to modify data against a related model instance within the django admin. To me this sounds like more specifically an Inline (click here for documentation)
So in your case, you would need to create something like the following to your admin.py file:
class SyncedBookInline(admin.TabularInline):
model = BookInline
#admin.Register(Book)
class BookAdmin(admin.ModelAdmin):
# all your model admin settings
inlines = [SyncedBookInline]
Additional Info:
The Inline solution should still work for you. Please see the working code listed below:
models.py:
from django.db import models
class Hero(models.Model):
name = models.CharField(max_length=50)
class HeroAcquaintance(models.Model):
name = models.CharField(max_length=50)
hero = models.OneToOneField(Hero, on_delete=models.CASCADE)
admin.py:
from django.contrib import admin
from .models import *
class HeroAcquaintanceInline(admin.TabularInline):
model = HeroAcquaintance
#admin.register(Hero)
class HeroAdmin(admin.ModelAdmin):
list_display = (
'name',
)
inlines = [HeroAcquaintanceInline]
#admin.register(HeroAcquaintance)
class HeroAcquaintanceAdmin(admin.ModelAdmin):
list_display = (
'name',
)
Screenshot:
I use Django 1.9 and Python 2.7.
My app has four models. Each "trip" is made of several "steps" chosen by the visitor, which relate to "places", which may have several related "Picture".
class Trip(models.Model):
title = models.CharField(max_length=140, blank=True)
class Step(models.Model):
theplace = models.ForeignKey(ThePlace)
trip = models.ForeignKey(Trip)
class ThePlace(models.Model):
name = models.CharField(max_length=200)
class Picture(models.Model):
file = models.ImageField(upload_to="pictures")
slug = models.SlugField(max_length=100, blank=True)
theplace = models.ForeignKey(ThePlace, null=True, blank=True)
I would like to retrieve all "Picture" objects which are related to a specific Trip, using an existing "selectedtrip" queryset:
selectedtrip = Trip.objects.filter(author=request.user)[0]
pictures = selectedtrip.step_set.all().theplace.picture_set.all()
Django displays the following error:
"AttributeError: 'QuerySet' object has no attribute 'theplace'"
Any idea why ?
Because all() returns a queryset, which is a collection of items; theplace is an attribute on an individual Step, not on the collection.
The way to do this type of query is to start from the class you want to retrieve, and follow the relationships within the query using the double-underscore syntax. So:
Picture.objects.filter(theplace__step__trip=selectedtrip)
I am trying to create the following models. There is a ManyToMany relation from Entry to AUTH_USER_MODEL via the EntryLike intermediate model.
class BaseType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creation_time = models.DateTimeField(auto_now_add=True)
last_update_time = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Title(BaseType):
text = models.CharField(max_length=100)
description = models.TextField()
class EntryLike(BaseType):
entry = models.ForeignKey(Entry)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
liked_by_users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='EntryLike', through_fields=('entry', 'user'))
Running migrations on the above model scheme throws the error: AttributeError:'str' object has no attribute 'meta'.
Any help in resolving this error would be highly appreciated. Am new to Django & Python, but not to Web Development.
The issue is that settings.AUTH_USER_MODEL is almost certainly not a model instance. It's probably a string that constrains the choices another model can make - settings would be a strange place to leave a model definition.
To do a MTM between the user model and your field above you need need to do:
from django.contrib.auth.models import User
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(User)
def __str__(self):
return self.title
I've added the str function so that it gives a more sensible return when you're manipulating it in admin/shell.
I'd also question whether you need the second set of fields (removed here), as you can use select related between the Entry and EntryLike join table, without any duplication of the fields - you can probably go that way, it's just a bit unnecessary.
Lastly, I'd note that the way I'm using it above just uses the default User object that comes with Django - you may wish to customise it. or extend the base class as you've done here with your own models' base class.
(All of this is predicated on AUTH_USER_MODEL not being a model instance - if it is, can you post the model definition from settings.py? )