so I have 4 models
class User(models.Model):
userID = models.CharField(pk = True)
......
class Producer(models.Model):
userID = models.OneToOneField('Users.User',on_delete=CASCADE,primary_key=True)
.....
class Buyer(models.Model):
userID = models.OneToOneField('Users.User',on_delete=CASCADE,primary_key=True)
.....
class Inventory(models.Model):
item_id = models.UUIDField(primary_key=True,auto_created=True,default=uuid.uuid4)
producerID = models.ForeignKey('Producers.Producer',on_delete=CASCADE)
.....
class Cart(models.Model):
userID = models.OneToOneField(Buyer, on_delete = CASCADE,primary_key = True)
last_updated = models.DateTimeField(auto_now = True)
class Cart_Item(models.Model):
cart_item_id = models.UUIDField(primary_key=True,auto_created=True,default= uuid.uuid4)
item_id = models.ForeignKey('Inventory.Inventory', on_delete= SET_NULL,null=True)
userID = models.ForeignKey(Cart,on_delete=CASCADE)
......
I then have a post-only view which processes all cart Items in order to create an order as follows
class PlaceOrderView(generics.CreateAPIView):
def post(self, request, *args, **kwargs):
user = request.user
cart = Cart_Item.objects.select_for_update().filter(userID = user).order_by('item_id__producerID')
order = {'producer':'',
'items': []
}
for item in cart:
if order['producer'] == item.values('item_id.producerID'):
order['items'].append(item)
else:
self.placeOrder(order)
order['producer'] = item.values('item_id.producerID')
order['items'] = []
order['items'].append(item)
def placeOrder(self,order):
with transaction.atomic():
#Business logic on order.
What Im trying to do is to group all cart Items by items owned by specific producers, and then place an order for that group of cart Items. Where I am having trouble is in accessing the nested field "producerID" of cart Item, which needs to be done in order to group all of my cart Items.
My placeOrder method, uses the cartItem object and so they are passed directly in the function. Currently I am serializing cart Items in the for loop just to compare the producerID's but this feels inefficient. I've read django documentation on the topic of fields, but there is not much support for nested fields. Some simple explanation on the topic would be great!
.values() is a queryset method, but since you are iterating the qs and working with each individual item you dont need it. If you have and Item you should be apple to access the fk relation
Have you tried:
order['producer'] = item.itemId.producerID
Related
I have a very simple ( with a first look) problem. Case - A product can be sold in a several places(shops), and every product can be represented in a single shop with a different categories and sub categories ( That is why category linked via ForeignKey with Assortment twice).
So here is My Assortment model, with several FKs.
class Assortment(models.Model):
category = models.ForeignKey('category.Category', null=True, blank=True, default=None,related_name='assortment_child')
parent_category = models.ForeignKey('category.Category', null=True, blank=True, default=None,related_name='assortment_parent')
product = models.ForeignKey(Product)
shop = models.ForeignKey(Shop)
View, based on rest_framework.generics.ListAPIView
class InstitutionTreeCategories(generics.ListAPIView):
"""Resource to get shop's tree of categories."""
serializer_class = serializers.InstitutionCategoriesSerializer
def get_queryset(self):
shop = self.get_shop()
return Category.objects.filter(assortment_parent__shop=shop).distinct()
And finally, serializers
class CategoryListSerializer(serializers.ModelSerializer):
class Meta:
"""Meta class."""
model = Category
fields = ('id', 'name', 'image')
class CategoriesTreeSerializer(CategoryListSerializer):
# childs = CategoryListSerializer(many=True, source='assortment_child__parent_category')
childs = serializers.SerializerMethodField()
class Meta(CategoryListSerializer.Meta):
"""Meta class."""
fields = ('id', 'name', 'image', 'childs')
def get_childs(self, obj):
qs = Category.objects.filter(assortment_child__parent_category=obj.id).distinct()
return CategoryListSerializer(qs, many=True, context=self.context).data
And i need to show Category Tree for a one single shop with my API.
But the problem is - If I use serializer.SerializerMethodField - it works, but too many queries (for every parent category). I tried to avoid it using 'source' option with my 'CategoryListSerializer' by I can't make it. Every time, I get - 'Category' object has no attribute assortment_child__parent_category. In a shell model i've tried
In [8]: cat.assortment_parent.values('category').distinct()
Out[8]: (0.003) SELECT DISTINCT "marketplace_assortment"."category_id" FROM "marketplace_assortment" WHERE "marketplace_assortment"."parent_category_id" = 4 LIMIT 21; args=(4,)
<AssortmentQuerySet [{'category': 3}]>
So - category object has this attributes, of course it does, i used it a get_childs method. So question is - how i can use it with serializer.ModelSerializer and it's source option? ( Of course using select_related method with queryset, to avoid excess queries).
by source option you should use . in instead of __:
childs = CategoryListSerializer(many=True, source='assortment_child.parent_category')
but still you will has many queries, to fix it you should use prefetch-related
def get_queryset(self):
shop = self.get_shop()
qs = Category.objects.filter(assortment_parent__shop=shop).all()
return qs.prefetch_related('assortment_child').distinct()
more detail you can read in the how-can-i-optimize-queries-django-rest-framework
I had the similar problem and the best solution I have found is to do some manual processing in order to receive desired tree representation.
So firstly we fetch all Assortment for shop and then build the tree manually.
Let's look at the example.
def get_categories_tree(assortments, context):
assortments = assortments.select_related('category', 'parent_category')
parent_categories_dict = OrderedDict()
for assortment in assortments:
parent = assortment.parent_category
# Each parent category will appear in parent_categories_dict only once
# and it will accumulate list of child categories
if parent not in parent_categories_dict:
parent_data = CategoryListSerializer(instance=parent, context=context).data
parent_categories_dict[parent] = parent_data
parent_categories_dict[parent]['childs'] = []
child = assortment.category
child_data = CategoryListSerializer(instance=child, context=context).data
parent_categories_dict[parent]['childs'].append(child_data)
# convert to list as we don't need the keys already - they were used only for matching
parent_categories_list = list(parent_categories_dict.values())
return parent_categories_list
class InstitutionTreeCategories(generics.ListAPIView):
def list(self, request, *args, **kwargs):
shop = self.get_shop()
assortments = Assortment.objects.filter(shop=shop)
context = self.get_serializer_context()
categories_tree = get_categories_tree(assortments, context)
return Response(categories_tree)
All in single DB query.
The problem here is that there is no explicit relation between category and parent_category. If you define ManyToManyField in Category using Assortment as through intermediate model, you will get an access which Django can understand, so you would just use attribute childs on Category for example. However this will still return all children (the same would happen if your source example works) categories, ignoring shop, so some clever Prefetch would have to be done to achieve correct results. But I believe manual "join" is simpler.
you need to use prefetch_related along with serializer method field
serializer:
class CategoriesTreeSerializer(CategoryListSerializer):
children = serializers.SerializerMethodField()
class Meta(CategoryListSerializer.Meta):
fields = (
'id',
'name',
'image',
'children'
)
def get_children(self, obj):
children = set()
for assortment in obj.assortment_parent.all():
children.add(assortment.category)
serializer = CategoryListSerializer(list(children), many=True)
return serializer.data
your get queryset method:
def get_queryset(self):
shop = self.get_shop()
return (Category.objects.filter(assortment_parent__shop=shop)
.prefetch_related(Prefetch('assortment_parent', queryset=Assortment.objects.all().select_related('category')
.distinct())
Is it possible to filter a queryset in custom manager to filter differently for different Users. I want one employee should have access to data in a particular range of dates only.
class UserWiseManager(models.manager)
.
.
class ItemsSold(models.Model):
date = model.DateField()
item = models.CharField(max_length=50)
objects = UserWiseManager()
You can pass User instance directly to the manager, like this:
class UserWiseQuerySet(models.QuerySet)
def for_user(self, user):
if user.is_anonymous():
return self.filter(date__gte=(now() - timedelta(days=1))
else:
return self
UserWiseManager = UserWiseQuerySet.as_manager()
class ItemsSold(models.Model):
date = model.DateField()
item = models.CharField(max_length=50)
objects = UserWiseManager()
def view(request):
items = ItemsSold.objects.for_user(request.user)
I need some conceptual help understanding how this idea would work. If I have a user make a list and they can add infinite items to each list, how would I want to map out the model?
In my list model I have user = ForeignKey(User), so each instantiation of this list model is attributed to the user currently logged in and they can have a series of lists. However, if they want to add an item to the list, would I have a new model called add_item and make the list_name = foreignkey(user.listname)?
#New List Model
class NewList(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100, default = "Enter List Name")
picture = models.ImageField(upload_to='profile_images', blank=True)
def __unicode__(self):
return self.list_name
#New Item Model
class NewItem(models.Model):
lists = models.ForeignKey(NewList)
# list_name = models.OneToOneField(User.list_name)
def __unicode__(self):
return self.user.username
If I understands right,
class MyList(models.Model):
user = ForeignKey(User)
list_name = CharField(...)
....
def list_items(self):
return self.mylistitems_set.all()
class MyListItem(models.Model):
mylist = ForeignKey(MyList)
item_name = .....
....
A user may create as many lists an he/she wants and may add any number of items to a specific list. So logic is you create any number of lists (a record on MyList) and add as many items as you want to it through MyListItem.
Following is a good way to do this:
alist = MyList(......)
alist.mylistitem_set.create(...) # add an item to alist
Django Reverse relations documentation is here
Update: Taking all lists of a specific user
user = User.objects.get() # a specific user
user.mylist_set.all() # returns all lists belong to that user.
You have to read more about reverse relations in django.
Sounds about right. If your model is eg named MyList, then:
my_user = ... # obtain a user object
my_item = MyList(user=my_user)
my_item.save()
I iterate through a list of Block objects, instantiate a ModelForm for each of them with a mapping dictionary that links a block_type to a ModelForm model, and then append the form to a list which I pass off to a template for display.
for block in blocks:
block_instance = block_map[block.block_type].objects.get(id=block.id)
new_form = block_forms[block.block_type]
new_form_instance = new_form(
request.user,
request.POST or None,
instance=block_instance,
prefix = block.id
)
form_zones.append(new_form_instance)
Later, while checking request.POST I validate each form
if request.POST.get("save_submit"):
for zone_form_check in story_zones:
for block_form_check in zone_form_check:
if block_form_check.is_valid():
print(block_form_check.cleaned_data.get("content"))
saved = block_form_check.save()
print(saved.content)
valid = True
if valid:
return redirect("Editorial:content", content_id=content_id)
cleaned_data.get("content") produces the updated data, but even after calling save() on the valid form, saved.content produces the object's old content attribute. In other words, a valid form is having save() called upon it, but it is not saving.
One of the forms in question (and currently my only one) is:
class Edit_Text_Block_Form(ModelForm):
content = forms.CharField(widget = forms.Textarea(
attrs = {
"class": "full_tinymce"
}),
label = "",
)
class Meta:
model = TextBlock
fields = []
def __init__(self, user, *args, **kwargs):
self.user = user
super(Edit_Text_Block_Form, self).__init__(*args, **kwargs)
The model in question is a TextBlock, which inherits from a Block objets. Both of those are below:
class Block(models.Model):
zone = models.ForeignKey(Zone)
order = models.IntegerField()
weight = models.IntegerField()
block_type = models.CharField(max_length=32, blank=True)
class Meta:
ordering = ['order']
def delete(self, *args, **kwargs):
# Calling custom delete methods of child blocks
child = block_map[self.block_type].objects.get(id=self.id)
if getattr(child, "custom_delete", None):
child.custom_delete()
# Overriding delete to check if there are any other blocks in the zone.
# If not, the zone itself is deleted
zones = Block.objects.filter(zone=self.zone).count()
if zones <= 1:
self.zone.delete()
# Children of Block Object
class TextBlock(Block):
content = models.TextField(blank=True)
Any ideas for why calling saved = block_form_check.save() isn't updating my model?
Thanks!
I think this is because you've effectively excluded all the model fields from the form by setting fields = [] in the form's Meta class. This means that Django no longer relates the manually-defined content field on the form with the one in the model.
Instead, set fields to ['content'], and it should work as expected.
TL;DR form name cannot start with a number as per html4 specs
Try prefix = "block_%s" % block.id
What I'm trying to do is a 2 tier search with drop down menus using Select widget, the results will be a listing of the fields from my Meta.model. the first Tier is a a State listing from State.model. Upon a select it is supposed to list out all of the cities with in the selected state, the problem I'm having (and I think its due to my lack of knowledge) is that the city listing is not filtered but a listing of all cities in my database regardless of state. I'm not sure where or how to pass my variable to be able invoke my .filter() statement.
models.py
class Meta(models.Model):
rcabbr = models.CharField(max_length = 15)
slug = models.SlugField(unique=False)
state = models.ForeignKey('State')
rc_state = models.CharField(max_length = 3)
oerp = models.CharField(max_length=18)
subgrp = models.SlugField()
sonus_pic = models.CharField(max_length=8)
ems = models.CharField(max_length=14)
agc = models.CharField(max_length=14)
def __unicode__(self):
return self.rcabbr
class State(models.Model):
name = models.CharField(max_length=2)
slug = models.SlugField(unique=True)
state_long = models.CharField(max_length=15)
owning_site = models.CharField(max_length=12)
def __unicode__(self):
return self.name
return self.state_long
forms.py
class states(forms.Form):
invent = [(k.name,k.state_long) for k in State.objects.all()]
rclist = forms.ChoiceField(widget=forms.Select, choices=invent)
class rateCenter(forms.Form):
invention = [(k.id,k.rcabbr,k.rc_state) for k in Meta.objects.all()]
rcviews = forms.ChoiceField(widget=forms.Select, choices=invention)
views.py
def StateAll(request):
""" This lists out all of the states within the database that
are served"""
rclist = states()
return render(request, 'statelist.html',{'rclist': rclist})
def RcView(request):
""" this should list all the rateCenters within
the state that was selected in StateAll() """
rclist = request.GET['rclist']
forms = rateCenter()
return render(request, 'rclist.html',{'forms': forms})
Logic tells me I should to do my .filter() statement in the forms.py but unsure how to pass the result from the request.GET in StateAll() view. I do have the debug_toolbar installed so I can see the variable u'rclist' and the value u'LA' (my test state). I had this working 100% using hyperlinks however the size of my test database is miniscule in comparison to what is going to be in the production version and HREF's are just not possible.
my understanding is:
ChainedForeignKey(LinkedModel, LinkedModel.field = "field in first Tier", chained_model_field = "current model_field")
so simple model should I think be something like this?
def State(model.models):
name = models.CharField(max_length=20) #this is the state abbreviation
state_long = models.CharFeild(max_length=20)#this is state long form
def Meta(model.models):
state = models.CharField(max_length=20)
slug = models.SlugField(unique = False) #same values as rcabbr
rcabbr = ChainedForeignKey(State, chained_field = "state_long",
chained_model_field = "slug")
.....
Does that look about right........so the First Field in the drop down should be the State_long, once selected the next should be the slug?. at which time the slug should be passed to my urls and the views for the that final page.
I am going to try this however I'm not 100% sure how to do my views and if I need to do something with forms page or does this cover it? The documentation is not user friendly for someone new to this so any input would be most appreciated!
There are many third party libraries django-smart-selects and dajax come to mind - that will automate this for you along with provide you the necessary javascript to filter the form fields on the fly.
If you are investigating those, here is how you would do it with just the django forms:
class RateCenterForm(forms.Form):
rate_center = forms.ModelChoiceField(queryset=Meta.objects.all())
def __init__(self, *args, **kwargs):
state = kwargs.pop('state')
super(RaterCenterForm, self).__init__(*args, **kwargs)
self.fields['rate_center'].queryset = Meta.objects.filter(state=state)
A ModelChoiceField is a select drop down that takes its values from a model.
Now in your view, you would call it like this:
def show_rate_centers(request):
form = RateCenterForm(state='SomeState')
# .. your normal logic here