Add parent field in django admin forms - python

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,]
...

Related

Django-filter how to show only some objects on dropdown?

My site simply works like this: every Manager can have some SubManagers, those SubManagers can have some Agents (so the Agents are indirectly related to the Manager, see models.py to understand better the relations between them). I want to show in the Manager's profile page (see views.py) all the MembershipCard created by his/her related Agents. I'm trying to implement a filter to search, for example, cards created by a specific Agent, i'm able to do this but i would like to show in the dropdown only the Agents related to the Manager, the dropdown list now shows all Agents in the database
models.py
class StandardProfile(models.Model):
name = models.CharField(max_length=200, null=True)
surname = models.CharField(max_length=200, null=True)
phone_number = models.CharField(max_length=200, null=True)
class Meta:
abstract = True
class Manager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
class SubManager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Capo, null=True, on_delete = models.SET_NULL)
class Agent(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Manager, null=True, on_delete = models.SET_NULL)
subManager = models.ForeignKey(SubManager, null=True, blank=True, on_delete = models.SET_NULL)
class MembershipCard(models.Model):
agent = models.ForeignKey(Agent, null=True,blank=True, on_delete = models.SET_NULL)
client = models.ForeignKey(Client, null=True,blank=True, on_delete = models.SET_NULL)
creation_date = models.DateTimeField(auto_now_add=True, null=True)
activation_date = models.DateTimeField(null=True,blank=True)
expiration_date = models.DateTimeField(null=True,blank=True)
views.py
#login_required(login_url='login')
def profilePage(request, pk): #www.mysite.com/profilePage/<pk>
user = User.objects.get(id=pk) #getting the user from <pk>
cards = MembershipCard.objects.filter(agent__manager=user.manager)
myFilter = MembershipCardFilter(request.GET,queryset=cards,user=user)
cards = myFilter.qs
#page_obj is used for Pagination, and contains the cards, i removed this part of code for better readability, can add it if needed
context = {'page_obj': page_obj,"user": user,"myFilter":myFilter}
return render(request, 'polls/profilePage.html',context)
filters.py
class MembershipCardFilter(django_filters.FilterSet):
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
By reading answers to similar questions i think i have to modify the __init__ method in the CardFilter class, i've tried to adapt some answers to my case but it didn't work for some reasons . Any anser/comment is appreciated!
PS: I don't know if the title is clear, feel free to suggest a better one
You can try feeding the agent dropdown during init like (not tested!):
class MembershipCardFilter(django_filters.FilterSet):
agent= django_filters.ModelChoiceFilter(
queryset=Agent.objects.none(),
)
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
def __init__(self, *args, **kwargs):
user = kwargs.get("user")
agents = Agent.objects.filter(manager__user=user)
super().__init__(*args, **kwargs)
self.filters["agent"].queryset = agents

Django annotate field with queryset of objects

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)

Django Rest API ManyToMany gives nothing [] in API

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.

Django Rest Framework requieres as not null look up field

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?

django rest serialize level 2 field

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.

Categories

Resources