I'm developing recipe and ingredient models.
models.py:
class Recipe(models.Model):
"""Recipe structure"""
author = models.ForeignKey(
User, on_delete=models.CASCADE
)
title = models.CharField(max_length=40, unique=True)
picture = models.ImageField(blank=True, null=True)
text = models.TextField(max_length=100, blank=True, null=True)
components = models.ManyToManyField('Component')
tag = MultiSelectField(max_length=10, choices=tags)
cooking_time = models.IntegerField(
validators=[MinValueValidator(1, 'Value cannot be lower than 1')]
)
slug = models.SlugField()
def __str__(self):
return self.title
class Meta:
db_table = 'recipes'
verbose_name = 'Рецепт'
verbose_name_plural = 'Рецепты'
class Component(models.Model):
"""Component structure"""
title = models.CharField(max_length=50, unique=True)
unit = models.CharField(max_length=10, choices=units)
def __str__(self):
return self.title
class Meta:
db_table = 'components'
verbose_name = 'Ингредиент'
verbose_name_plural = 'Ингредиенты'
class Component_quantity(models.Model):
"""Table linking quantity with recipe and component together"""
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
component = models.ForeignKey(Component, on_delete=models.CASCADE)
quantity = models.DecimalField(
max_digits=5,
decimal_places=1,
verbose_name='Количество',
validators=[MinValueValidator(1)]
)
class Meta:
db_table = 'quantity_and_recipe_linking_table'
unique_together = ('recipe', 'component')
verbose_name = 'Ингредиент в рецепте'
verbose_name_plural = 'Ингредиенты в рецепте'
The problem is to link the recipe and the component in the Component_quantity model so that in the component field it is possible to select only those entities that are specified in the recipe itself. Is it possible to do this?
I would recommend to put the quantity field directly into the Component model. Another way to do it is to add a quantity foreignkey to the Component model and then remove the recipe and component foreignkey from the Component_quantity model.
Related
Suppose I have two models:
ModelA and ModelB
How can I annotate a queryset of ModelB with objects from ModelA?
queryset = ModelB.objects.filter(...).annotate(models_a=Subquery(ModelA.objects.filter(...)))
In order to have queryset.models_aas a Queryset of objects ModelA.
Thanks you all!
EDIT:
This are my models:
class Allergen(models.Model):
name = models.CharField(_('Allergen name'), max_length=20, choices=ALLERGENS,
help_text=_('Product allergen. Example: gluten'), unique=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255, help_text=_('Product name'))
supplier = models.ForeignKey(Supplier, blank=True, null=True, related_name='supplier_products',
on_delete=models.CASCADE)
allergens = models.ManyToManyField(Allergen, blank=True, related_name='product_allergens')
unit = models.CharField(_('Unit'), max_length=20, choices=UNITS, default='g')
price = models.FloatField(_('Sale price'), default=0)
unit_price = models.FloatField(_('Unit price'))
class Meta:
ordering = ['name', ]
indexes = [models.Index(fields=['name', 'supplier']), ]
class Recipe(models.Model):
sections = models.ManyToManyField(Section, related_name='recipes', blank=True)
title = models.CharField(_('Recipe title'), max_length=255, help_text=_('Recipe. Example: american chicken salad'),
blank=True)
unit = models.CharField(_('Unit'), max_length=20, choices=UNITS)
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='user_recipes')
class Meta:
ordering = ['title']
indexes = [models.Index(fields=['title'])]
class IngredientRecipe(models.Model):
name = models.CharField(_('Name'), max_length=255)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredients')
products = models.ManyToManyField(Product, related_name='ingredient_products')
quantity = models.FloatField(_('Quantity'))
class Meta:
ordering = ['-id']
unique_together = ('name', 'recipe')
indexes = [models.Index(fields=['name', 'recipe'])]
I'm trying to include the allergens related with the recipe.
If you are working with just one recipe, you can just add a model property that returns the related allergens of the recipe by using the reverse relations like this:
class Recipe(models.Model):
...
#property # or consider using cached_property
def allergens(self):
return Allergen.objects.filter(
product_allergens__ingredient_products__recipe=self,
)
Then you can access the allergens through the instance:
recipe = Recipe.objects.first()
print(recipe.allergens)
i'm trying to post a transaction via django rest framework, however it shows error in django log as below:
IntegrityError at /api/item_trans/
NOT NULL constraint failed: chemstore_itemtransaction.bin_code_id
it has no problem if I post the same data from the Django admin web.
therefore I suppose the problem has happened at DRF
any help is welcome, thank you
models.py
class BinLocation(models.Model):
bin_code = models.CharField(max_length=10, unique=True)
desc = models.CharField(max_length=50)
def __str__(self):
return self.bin_code
class Meta:
indexes = [models.Index(fields=['bin_code'])]
class ItemMaster(models.Model):
item_code = models.CharField(max_length=20, unique=True)
desc = models.CharField(max_length=50)
long_desc = models.CharField(max_length=150, blank=True)
helper_qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
def __str__(self):
return self.item_code
class Meta:
verbose_name = "Item"
verbose_name_plural = "Items"
indexes = [models.Index(fields=['item_code'])]
class ItemTransaction(models.Model):
# trace_code YYMMDDXXXX where XXXX is random generated
trace_code = models.CharField(max_length=20, unique=False)
item_code = models.ForeignKey(
ItemMaster, on_delete=models.CASCADE, related_name='+', blank=False, null=False)
datetime = models.DateTimeField(auto_now=False, auto_now_add=False)
qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
action = models.CharField(
max_length=1, choices=ACTION, blank=False, null=False)
bin_code = models.ForeignKey(
BinLocation, related_name='+', on_delete=models.CASCADE, blank=False, null=False)
remarks = models.TextField(blank=True)
def __str__(self):
return f"{self.trace_code} {self.datetime} {self.item_code} {dict(ACTION)[self.action]} {self.qty} {self.unit} {self.bin_code}"
serializers.py
class ItemMasterSerializer(serializers.ModelSerializer):
class Meta:
model = ItemMaster
fields = '__all__'
class ItemTransactionSerializer(serializers.ModelSerializer):
item_code = serializers.SlugRelatedField(
slug_field='item_code',
read_only=True
)
bin_code = serializers.SlugRelatedField(
slug_field='bin_code',
read_only=True,
allow_null=False
)
class Meta:
model = ItemTransaction
fields = '__all__'
You might need to use 2 fields, one for reading data and the other for creating and updating your data with its source to the main. In your case you could try this:
class ItemTransactionSerializer(serializers.ModelSerializer):
item_code_id = ItemMasterSerializer(read_only=True)
item_code = serializers.PrimaryKeyRelatedField(
queryset=ItemMaster.objects.all(),
write_only=True,
source='item_code_id'
)
bin_code_id = BinLocationSerializer(read_only=True
bin_code = serializers.PrimaryKeyRelatedField(
queryset= BinLocation.objects.all(),
write_only=True,
source='bin_code_id'
)
Since you have null=False in both of your ForeignKeys, DRF expects the corresponding ID. You seem to be getting the error NOT NULL constraint because you are not passing the ID in DRF. So you need to fix that for both bin_code_id and the item_code_id.
I have a created Django-CMS Plugin with a ManytoManyField to another model. When creating the Plugin on the Front-End, i want the user to be able to filter the ManytoManyField list (which might be too long).
By the way this future is already on Django admin:
class ModelAdmin(admin.ModelAdmin):
list_filter = ('field_name', )
form = PartnerLogoTableForm
Is it possible to have something similar like that on my
cms_plugins.py:
class PartnerLogoTablePlugin(CMSPluginBase):
model = LogoTablePlugin
form = LogoTablePluginForm
name = _('LogoTable Plugin')
render_template = False
search_fields = ('description',)
def render(self, context, instance, placeholder):
self.render_template = 'aldryn_logo_tables/plugins/%s/logotable.html' % instance.style
context.update({
'object': instance,
'placeholder': placeholder,
})
return context
plugin_pool.register_plugin(PartnerLogoTablePlugin)
models.py:
class PartnerLogoTable(models.Model):
name = models.CharField(_('Partner name'), max_length=255)
image = FilerImageField(verbose_name=_('Image'), null=True, blank=True, on_delete=models.SET_NULL)
partner_url = models.TextField(_('Partner url'), null=True, blank=True, validators=[URLValidator()])
is_active = models.BooleanField(_('Is active'), blank=True, default=True)
created_at = models.DateTimeField(_('Created at'), auto_now_add=True)
updated_at = models.DateTimeField(_('Updated at'), auto_now=True)
order = models.IntegerField(_('Order'), null=True, blank=True)
class Meta:
verbose_name = _('Partner Logo')
verbose_name_plural = _('Partner Logos')
ordering = ['order']
def __str__(self):
return self.name
class LogoTablePlugin(CMSPlugin):
DEFAULT = 'default'
LOGOTABLE_CHOICES = [
(DEFAULT, _('Default')),
]
description = models.CharField(_('Description'), max_length=255, null=True, blank=True)
partner = models.ManyToManyField(PartnerLogoTable, verbose_name=_('Partner'))
logo_per_row = models.IntegerField(_('Logo per line'), default=1, null=True, blank=True,
validators=[MaxValueValidator(4), MinValueValidator(1)],
help_text=_('Number of logos to be displayed per row'))
style = models.CharField(_('Style'), choices=LOGOTABLE_CHOICES + get_additional_styles(), default=DEFAULT,
max_length=50, blank=True, null=True, )
class Meta:
verbose_name = _('Partner Logo Plugin')
verbose_name_plural = _('Partner Logo Plugins')
def __unicode__(self):
return u'%s' % self.description
forms.py
class LogoTablePluginForm(forms.ModelForm):
model = LogoTablePlugin
def clean_style(self):
.....
return style
class PartnerLogoTableForm(forms.ModelForm):
model = PartnerLogoTable
def clean(self):
....
return self.cleaned_data
This is how the plugin looks now
ModelSelect2Multiple from django-autocomplete-light seems perfect for your use case.
I have two models:
class Album(models.Model):
code = models.CharField(max_length=10, primary_key=True, default=_create_access_code, verbose_name=_("Id"))
name = models.CharField(max_length=200, verbose_name=_("Name"))
description = models.TextField(null=True, blank=True, verbose_name=_("Description"))
company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name='albums', verbose_name=_("Company"))
access_code = models.CharField(max_length=10, default=_create_access_code, verbose_name=_("Internal Use"))
class Meta:
verbose_name = _("Album")
verbose_name_plural = _("Albums")
def __str__(self):
return "[{}] {} ({})".format(self.pk, self.name, self.company.id)
class Photo(models.Model):
name = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Name"))
album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name=_("Album"))
photo = models.ImageField(verbose_name=_("Photo"))
class Meta:
verbose_name = _("Photo")
verbose_name_plural =_("Photos")
def __str__(self):
return "[{}] {}".format(self.pk, self.name)
I am trying to make a post to the ModelViewSet for model Albums, but I get an error indicating that field photos is required. Even the OPTIONS method indicates it es required.
How can I instruct DRF for not considering look up fields as required? Is it some serializer setting?
You can add required=False to fields in the serializer.
photos = PhotoSerializer(many=True, required=False)
Something like this. Can you post you serializers?
I have DjangoTable for this model:
class Mark(models.Model)
id_mark = models.AutoField(primary_key=True, verbose_name='Id запису')
id_student = models.ForeignKey(Student, blank=False, null=False, default=None, on_delete=models.CASCADE,verbose_name='Студент')
id_subject = models.ForeignKey(Subject, blank=False, null=False, default=None, on_delete=models.CASCADE,verbose_name='Предмет')
mark = models.DecimalField(decimal_places=3, max_digits=5, blank=False, null=False, default=None, verbose_name='Оцінка')
class Meta:
verbose_name = 'Оцінка'
verbose_name_plural = 'Оцінки'
def __str__(self):
return "{} {} {}".format(self.id_student, self.id_subject, self.mark)
Table:
class Mark_Table(tables.Table):
class Meta:
model = Mark
exclude = ('id_mark',)
attrs = {'class': 'paleblue'}
I want to add column with values from this model:
class Subject(models.Model)
id_subject = models.AutoField(primary_key=True, verbose_name='Id предмету')
id_teacher = models.ForeignKey(Teacher, blank=False, null=False, default=None, on_delete=models.CASCADE,verbose_name='Вчитель')
name = models.CharField(max_length=32, blank=False, null=False, default=None, verbose_name='Назва предмету')
class Meta:
verbose_name = 'Предмет'
verbose_name_plural = 'Предмети'
def __str__(self):
return "{}".format(self.name)
Now I have fields Student Subject Mark, I want to add filed id_teacher from another model, and relationship must remain.
You can add foreign key value in table using table.Coulmn() and specifying accessor
like below:
id_teacher = tables.Column(accessor='id_subject.id_teacher')
So your Mark_Table will look like:
class Mark_Table(tables.Table):
# Define all the custom fields here including any override or adding other fields and include it in fields in meta class
id_teacher = tables.Column(accessor='id_subject.id_teacher')
class Meta:
model = Mark
fields = ['mark', 'id_teacher'] # specify all the fields you want to display here
attrs = {'class': 'paleblue'}
Read more about adding custom field at docs