at the moment I try to get recipes from my API. I have a Database with two tables one is with recipes and their ids but without the ingredients, the other table contains the ingredients and also the recipe id. Now I cant find a way that the API "combines" those. Maybe its because I added in my ingredient model to the recipe id the related name, but I had to do this because otherwise, this error occurred:
ERRORS:
recipes.Ingredients.recipeid: (fields.E303) Reverse query name for 'Ingredients.recipeid' clashes with field name 'Recipe.ingredients'.
HINT: Rename field 'Recipe.ingredients', or add/change a related_name argument to the definition for field 'Ingredients.recipeid'.
Models
from django.db import models
class Ingredients(models.Model):
ingredientid = models.AutoField(db_column='IngredientID', primary_key=True, blank=True)
recipeid = models.ForeignKey('Recipe', models.DO_NOTHING, db_column='recipeid', blank=True, null=True, related_name='+')
amount = models.CharField(blank=True, null=True, max_length=100)
unit = models.CharField(blank=True, null=True, max_length=100)
unit2 = models.CharField(blank=True, null=True, max_length=100)
ingredient = models.CharField(db_column='Ingredient', blank=True, null=True, max_length=255)
class Meta:
managed = True
db_table = 'Ingredients'
class Recipe(models.Model):
recipeid = models.AutoField(db_column='RecipeID', primary_key=True, blank=True) # Field name made lowercase.
title = models.CharField(db_column='Title', blank=True, null=True, max_length=255) # Field name made lowercase.
preperation = models.TextField(db_column='Preperation', blank=True, null=True) # Field name made lowercase.
images = models.CharField(db_column='Images', blank=True, null=True, max_length=255) # Field name made lowercase.
#ingredients = models.ManyToManyField(Ingredients)
ingredients = models.ManyToManyField(Ingredients, related_name='recipes')
class Meta:
managed = True
db_table = 'Recipes'
When there is no issue it has to be in the serializer or in the view.
Serializer
class IngredientsSerializer(serializers.ModelSerializer):
# ingredients = serializers.CharField(source='ingredients__ingredients')
class Meta:
model = Ingredients
fields = ['ingredient','recipeid']
class FullRecipeSerializer(serializers.ModelSerializer):
ingredients = IngredientsSerializer(many=True)
class Meta:
model = Recipe
fields = ['title','ingredients']
View
class FullRecipesView(generics.ListCreateAPIView):
serializer_class = FullRecipeSerializer
permission_classes = [
permissions.AllowAny
]
queryset = Recipe.objects.all()
This is at the moment my output
But I want e.g. the recipe with id 0 and all the ingredients which have also recipe id 0.
I really hope that you can help me. Thank you so much!
From the doc of ForeignKey.related_name,
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'.
So, change the related_name of Ingredients.recipeid field to
class Ingredients(models.Model):
# rest of the fields
recipeid = models.ForeignKey(
'Recipe',
models.DO_NOTHING,
db_column='recipeid',
blank=True,
null=True,
related_name="ingredients_ref" # Changed the related name
)
Then, migrate the database using python manage.py makemigrations and python manage.py migrate
Then, update your FullRecipeSerializer class as,
class FullRecipeSerializer(serializers.ModelSerializer):
ingredients_forward = IngredientsSerializer(many=True, source="ingredients")
ingredients_backward = IngredientsSerializer(many=True, source="ingredients_ref")
class Meta:
model = Recipe
fields = ['title', 'ingredients_forward', 'ingredients_backward']
Note that, here I have added two fields named ingredients_forward and ingredients_backward because there existing two types of relationships between Recipe and Ingredients and I am not sure which one you are seeking.
Related
i want to display the first entry of a queryset.
i try some answers here but it does´tn work on my code example.
here is my code:
class FeatureFilmAdmin(admin.ModelAdmin):
inlines = [
QuoteAndEffortSetInLine,
ProjectCompanySetInLine,
VendorVFXSetInLine,
VendorSetInLine,
StaffListSetInLine,
]
list_display = ["title", "program_length", "get_production_company_name"]
def get_production_company_name(self, Featurefilm):
return FeatureFilm.objects.filter(pk=Featurefilm.id).values(
"projectcompanyset__production_company__name"
)
so i actually want to display the production_company, from the first table of ProjectCompanySet in the admin as list_display.
so i actually want to display the production_company, from the first table of ProjectCompanySet in the admin as list_display. but with the code above, it will show me all production_company, if there are multiple ProjectcompanySet. What is displayed so far is also not the production_company name but the str function and not the data field itself. Here I would need help please.
here are the models of my problem:
class CompanyOrBranch(CompanyBaseModel):
name = models.CharField(
"Firma oder Niederlassung",
max_length=60,
blank=False,
)
class FeatureFilm(ProjectBaseModel):
class Meta:
verbose_name = "Kinofilm"
verbose_name_plural = "Kinofilme"
class ProjectCompanySet(models.Model):
feature = models.ForeignKey(
FeatureFilm,
on_delete=models.CASCADE,
null=True,
blank=True,
)
production_company = models.ForeignKey(
CompanyOrBranch,
related_name="production_company",
verbose_name="Produktionsfirma",
on_delete=models.SET_NULL,
blank=True,
null=True,
)
here is the output of my admin list:
I've setup a relationship using django's ForeignKey against 2 unmanaged tables like so:
class Product(BaseModel):
publish_name = models.CharField(unique=True, max_length=40)
# this works:
associated_country = models.ForeignKey('geonames.Countryinfo', models.DO_NOTHING, db_column='published_country', blank=True, null=True)
# this doesn't:
associated_continent = models.ForeignKey('geonames.Continentcodes', on_delete=models.DO_NOTHING, db_column='published_continent' blank=True, null=True)
class Meta:
managed = True
db_table = 'product'
class Continentcodes(models.Model):
code = models.CharField(max_length=2, primary_key=True, unique=True)
name = models.TextField(blank=True, null=True)
geoname_id = models.ForeignKey('Geoname', models.DO_NOTHING, blank=True, null=True, unique=True)
class Meta:
managed = False
db_table = 'geoname_continentcodes'
class Countryinfo(models.Model):
iso_alpha2 = models.CharField(primary_key=True, max_length=2)
country = models.TextField(blank=True, null=True)
geoname = models.ForeignKey('Geoname', models.DO_NOTHING, blank=True, null=True)
neighbours = models.TextField(blank=True, null=True)
class Meta:
ordering = ['country']
managed = False
db_table = 'geoname_countryinfo'
verbose_name_plural = 'Countries'
When I go to edit an entry in the django admin page for 'Products' I see this:
InvalidCursorName at /admin/product/6/change/ cursor
"_django_curs_140162796078848_sync_5" does not exist
The above exception (column geoname_continentcodes.geoname_id_id does
not exist LINE 1: ...ntcodes"."code", "geoname_continentcodes"."name",
"geoname_c...
^ HINT: Perhaps you meant to reference the column
"geoname_continentcodes.geoname_id"
It looks like it's trying to reference geoname_continentcodes.geoname_id_id for some reason. I have tried adding to='code' in the ForeignKey relationship, but it doesn't seem to effect anything.
Additionally, the associated_country relationship seems to work just fine if I comment out the associated_continent field. The associated_continent is the column that is giving some problem.
Here is some more context about what the table looks like in the database:
Removing the '_id' as the suffix is the fix. In this case geoname_id changed to geoname fixes this. Why? I have no idea. Django is doing something behind the scenes that is not clear to me. Here is the updated model:
class Continentcodes(models.Model):
code = models.CharField(max_length=2, primary_key=True, unique=True)
name = models.TextField(blank=True, null=True)
# remove '_id' as the suffix here
geoname = models.ForeignKey('Geoname', models.DO_NOTHING, blank=True, null=True, unique=True)
class Meta:
managed = False
db_table = 'geoname_continentcodes'
Django adds _id to the end to differentiate between the object and the id column in the table, geoname_id will return the table id where as geoname will return the object.
I have two models in Django with managed=False, because they are generated by SQL.
Django's Querysets based on these models do not generate the SQL queries they should. They add an '_ID' suffix to a column name that I never specified. This causes the queries to fail.
The models:
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255)
MATERIAL = models.ForeignKey('ColumnsPerMaterialStatic', on_delete=models.CASCADE)
class Meta:
db_table = "CDH_MATERIALS_PER_BATCH"
managed = False
class ColumnsPerMaterialStatic(models.Model):
MATERIAL = models.CharField(primary_key=True, null=False, max_length=255)
MATERIAL_LINK = models.CharField(null=False, max_length=255)
class Meta:
db_table = "CDH_COL_PER_MAT_STATIC"
managed = False
I want to filter the first model by BATCH, and find all corresponding second models that share the MATERIAL field.
The query looks like this:
qs = models.MaterialsPerBatch.objects.filter(
BATCH__in=['foo', 'bar']).values('BATCH', 'MATERIAL__MATERIAL_LINK')
I get this error: "django.db.utils.DatabaseError: ORA-00904: CDH_MATERIALS_PER_BATCH"."MATERIAL_ID": invalid identifier"
Inspecting qs.query shows that Django runs the following query in the background:
SELECT "CDH_MATERIALS_PER_BATCH"."BATCH", "CDH_COL_PER_MAT_STATIC"."MATERIAL_LINK"
FROM "CDH_MATERIALS_PER_BATCH"
INNER JOIN "CDH_COL_PER_MAT_STATIC"
ON ("CDH_MATERIALS_PER_BATCH"."MATERIAL_ID" = "CDH_COL_PER_MAT_STATIC"."MATERIAL")
WHERE "CDH_MATERIALS_PER_BATCH"."BATCH" IN ('foo', 'bar')
So the question is, why does Django turn "CDH_MATERIALS_PER_BATCH"."MATERIAL" into "CDH_MATERIALS_PER_BATCH"."MATERIAL_ID"?
I never specified any '_ID' suffix.
How do I tell Django not to add that suffix?
Django allows the db_column name to be specified, so it will not assume the _id is part of the name.
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255, db_column='BATCH')
MATERIAL = models.ForeignKey('ColumnsPerMaterialStatic', on_delete=models.CASCADE)
class Meta:
db_table = "CDH_MATERIALS_PER_BATCH"
managed = False
class ColumnsPerMaterialStatic(models.Model):
MATERIAL = models.CharField(primary_key=True, null=False, max_length=255, db_column='MATERIAL')
MATERIAL_LINK = models.CharField(null=False, max_length=255)
class Meta:
db_table = "CDH_COL_PER_MAT_STATIC"
managed = False
You specify the name of the column with the db_column=… parameter [Django-doc]:
class MaterialsPerBatch(models.Model):
BATCH = models.CharField(null=False, max_length=255)
MATERIAL = models.ForeignKey(
'ColumnsPerMaterialStatic',
on_delete=models.CASCADE,
db_column='MATERIAL'
)
class Meta:
db_table = 'CDH_MATERIALS_PER_BATCH'
managed = False
Normally the names of the fields are written in snake_case, not SCREAMING_SNAKE_CASE, so you might want to consider renaming BATCH and MATERIAL to batch and material and thus use the db_column to specify the name of the database column:
class MaterialsPerBatch(models.Model):
batch = models.CharField(
max_length=255,
db_column='BATCH'
)
material = models.ForeignKey(
'ColumnsPerMaterialStatic',
on_delete=models.CASCADE,
db_column='MATERIAL'
)
# …
Here are my two models:
class Provider(models.Model):
name = models.CharField(max_length=75, null=True, blank=True)
code = models.CharField(max_length=15)
provider_parent = models.ForeignKey('self', null=True, blank=True)
accounts = models.ManyToManyField('Account', blank=True)
data_types = models.ManyToManyField('DataType', blank=True,
through='ProviderDataType')
class Account(models.Model):
name = models.CharField(max_length=75, unique=True)
prefixes = models.ManyToManyField('AccountPrefix', blank=True)
Here is my admin.py
class ProviderAdmin(admin.ModelAdmin):
list_display = ('code', '__unicode__')
class AccountAdmin(admin.ModelAdmin):
list_display = ('__unicode__')
admin.site.register(Provider, ProviderAdmin)
admin.site.register(Account, AccountAdmin)
I was wondering if it is possible to have a selection of the parent provider when I try to add or update my account model and upon saving it. The Parent model has already set the account on its manytomany field
If I understood your question correctly you can use TubularInline. Like this:
class ProviderInline(admin.TabularInline):
model = Provider.accounts.through
extra = 1
class AccountAdmin(admin.ModelAdmin):
inlines = [ProviderInline,]
...
Hi I need to add a field in a serializer of a 2 level reference item.
I have the following model:
model.py:
class Company(models.Model):
companyName = models.CharField(max_length=50, blank=True)
class Poll(models.Model):
questionString = models.CharField(max_length=500, blank=True)
companyId = models.ForeignKey(Company, null=True, db_column='companyId', blank=True)
class PossibleAnswer(models.Model):
answerString = models.CharField(max_length=100, blank=True)
pollId = models.ForeignKey(Poll, null=True, db_column='pollId', blank=True,related_name='answers')
token = models.CharField(max_length=10, blank=True)
serializers.py:
class PossibleAnswerSerializer(serializers.ModelSerializer):
#companyId = serializers.RelatedField()
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId')
I want to make a Serializer for the PossibleAnswer object that has a field named company. How to make a this reference? Something similar to: pollId__companyId in a django query set filter.
Another solution...
class PossibleAnswerSerializer(serializers.ModelSerializer):
companyId = serializers.SerializerMethodField()
def get_companyId(self, obj):
return obj.pollId.companyId
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId',)
I the field is read-only you can easily achieve this with a serializers.Field, which accept dotted paths to the source.
Your Serializer would be:
class PossibleAnswerSerializer(serializers.ModelSerializer):
companyId = serializers.Field(source='pollId.companyId')
class Meta:
model = PossibleAnswer
fields = ('answerString', 'token', 'pollId', 'companyId')
I too agree with Erik, that naming model attributes with Id is a bad idea even though the DB representation is only the ID.