Django inline model formset with 2 models - django

First of all, please forgive for my newbie questions. I did copy most of the code, and try to understand from Django documents.
Code as below:
models.py
class Order(models.Model):
ORDER_CHOICES = (
('import', 'IMPORT'),
('export', 'EXPORT')
)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
order_type = models.CharField(max_length=6, choices=ORDER_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Item(models.Model):
def random_barcode():
return str(random.randint(10000000, 99999999))
type = models.ForeignKey(Type, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
item_name = models.CharField(max_length=50, help_text='Name of goods, max 50 characters')
barcode = models.CharField(max_length=8, default=random_barcode, unique=True)
production_date = models.DateField()
expired_date = models.DateField()
def __str__(self):
return self.item_type
forms.py
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ['order',]
fields = ['type', 'brand', 'item_name', 'production_date', 'expired_date']
ItemFormSet = inlineformset_factory(Order, Item, form=ItemForm, extra=1)
views.py
class CreatePO(CreateView):
model = Order
context_object_name = 'orders'
template_name = 'storages/create_po.html'
fields = ['order_type', 'storage',]
*#dun't know how to write below code....*
1st question: how to use inline formset to write the CreatePO view?
2nd question: I need my create PO template as below picture, how to add a "Quantity" field?
This kind of template need Javascript, right? Any alternative solution? I have no knowledge with javascript.

First of all, move the def random_barcode(): before def __str__(self): it looks so ugly formated code.
Then let's have a look in your pic, if you haven't proper experience with Javascript you can use Admin Views from Django, it's much more simple and supported by Django 2.1. Read more if you would like to give permission to everyone in a admin-views page https://docs.djangoproject.com/el/2.1/releases/2.1/#model-view-permission
So quantity will be just added inside Item class
quantity = models.PositiveSmallIntegerField(default=1)
Also for your form, in my opinion, you need modelform_factory, so I suggest to read this one https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#modelform-factory-function

Related

How to use model functions in Views for multiple instances in Django?

I have a Blog Post Model and I have defined a function to calculate the no of likes.
The Model is as follows ->
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
title = models.CharField(max_length=255)
description = models.CharField(max_length=1000,null=True)
Tags = models.CharField(max_length = 255,null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
Likes = models.ManyToManyField(to=User, related_name='Post_likes')
def __str__(self):
return self.title
def likesCount(self):
return self.Likes.count()
Now I am querying the Post Model from the DB to get all the Posts as follows ->
posts = Post.objects.select_related().prefetch_related('images_set','comments_post').annotate(Count('comments_post')).all()
Here when I loop over the posts I can call the likesCount function and it gives me the correct result as well but I want to return the No of likes to the template.
How can I do that?
in your template, try this:
{{ post.likes_set.count }}
and please make the field names lowercase, they are not Classes

django.core.exceptions.FieldError: Cannot resolve keyword 'date' into field. Join on 'date' not permitted

# Here is my models
This is my CustmerBuySell model DB designed.
class CustomerBuySell(models.Model):
customer = models.ForeignKey(CustomerAdd, on_delete=models.CASCADE)
customer_buy_sell_debit = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
customer_buy_sell_credit = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
description = models.CharField(max_length=250, blank=True)
date = models.DateField()
sms = models.BooleanField(default=False)
picture = models.ImageField(upload_to='customer_buy_sell_pics', default='images.png')
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
def __str__(self):
return self.customer.customer_name
class Meta:
verbose_name = "Customer BuySell"
verbose_name_plural = "Customer BuySell"
# Here, is my View.
This is the class-based APIView, which I have used. And try to use the aggregate query in this view.
class DailyCustomerBuySellAPIView(APIView):
def get(self, request):
customer_buy_sell = CustomerBuySell.objects.extra(select={'day': 'date( date )'}).values('day').order_by(
'date__date').annotate(available=Count('date__date'))
serializer = CustomerBuySellSerializer(customer_buy_sell, many=True)
return Response({"customer_buy_sell": serializer.data})
# And, finally here are my Serializers
I have no idea what's the problem! Please help me.
class CustomerBuySellSerializer(serializers.ModelSerializer):
# customer = CustomerAddSerializer()
class Meta:
model = CustomerBuySell
fields = '__all__'
def to_representation(self, instance):
representation = super(CustomerBuySellSerializer, self).to_representation(instance)
if instance.customer is not None:
customer_name = instance.customer.customer_name
previous_due = instance.customer.previous_due
representation['custo`enter code here`mer_name'] = customer_name
representation['previous_due'] = previous_due
return representation
There are many problems with your approach. Let me mention each of them one by one:
First of all remove date__date from your APIVIew
Before:
customer_buy_sell = CustomerBuySell.objects.extra(select={'day': 'date( date )'}).values('day').order_by(
'date__date').annotate(available=Count('date__date'))
Instead, write it as:
from django.db.models.functions import Extract
customer_buy_sell = CustomerBuySell.objects.annotate(day=Extract('date','day')).values('day').order_by('day')
if you need a count of the days then you can try
customer_buy_sell_count = customer_buy_sell.count()
Another thing that you are doing wrong is you pass a dict to serializer as you are already using values that return a dictionary of days and not object of CustomerBuySell so you do not need to pass it to serializer otherwise you have to do it according to your need.
In CustomerBuySellSerializer you are using a model serializer with __all__ while you are passing an extra fields day that is not part of it.
So in short there are so many syntax issues with your Django and Django Rest Framework.Great way to fix such issues is to set with an experience programmer so that he can improve the flow of the code. Later on you can focus on logic.
I suppose it is just a typo: Change date__date to date

How can I show a dropdown in my form with the title of the objects in the table? Django ModelForm

Hi I've been trying to create a simple app for workflows or binary protocols (yes or no instructions, very helpful sometimes)
I have created a simple form with ModelForm:
class BlocForm(forms.ModelForm):
class Meta:
model = Bloc
fields = [
'description',
'loop_child'
]
and my model looks like this:
class Loopeable(models.Model):
protocol = models.ForeignKey(Protocol, on_delete=models.CASCADE, null=True)
description = models.TextField()
pointing_at = models.IntegerField()
class Bloc(models.Model):
description = models.TextField()
protocol = models.ForeignKey(Protocol, on_delete=models.CASCADE)
parent = models.ForeignKey('self',on_delete=models.CASCADE, null=True, blank=True)
loop_child = models.ForeignKey(Loopeable, on_delete=models.CASCADE, null=True, blank=True)
question = models.BooleanField(default=False)
end_bloc = models.BooleanField(default=False)
And my problem is that instead of having this loop_chile dropdown list of ugly objects I would like to get the description in the Loopeable Model, is there a way to get some data that is not just "Loopeable Object (id_number)". I didn't add my views.py in order to keep the post short, but I implemented the for by simply doing something like:
form = BlocForm(request.POST or None)
if form.is_valid(): ...rest of the code
If anyone has some ideas I will be grateful!! Thanks...

Django Order_by Not working on FloatField

Order_by not working in FloatField type Django
models.py
class CourseCategory(models.Model):
category = models.CharField(max_length=100, unique=True, null=False)
description = models.TextField(blank=True)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return self.category
Coursetrack Model
class CourseTrack(models.Model):
category = models.ForeignKey(CourseCategory)
course_id = CourseKeyField(max_length=255, db_index=True)
tracks = models.FloatField(null=True, blank=True, default=None)
active = models.BooleanField(default=True)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return str(self.course_id)
TopCoursesCategory
class TopCoursesCategory(models.Model):
category = models.ForeignKey(CourseCategory)
class Meta(object):
app_label = "course_category"
def __unicode__(self):
return str(self.category)
I added here order_by(), as you can see but its not working.
view.py
def get_popular_courses_ids():
popular_category_id = CourseCategory.objects.filter(category='Popular')
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category=popular_category_id).order_by('tracks')
course_id_list = []
for course_id in popular_courses_ids:
course_id_list.append(course_id['course_id'])
return course_id_list
I think the query you have posted is wrong.
You have used the following lines.
popular_category_id = CourseCategory.objects.filter(category='Popular')
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category=popular_category_id).order_by('tracks')
In the first line, you have used filter and you have used the resulting variable as category= in your second query which you cannot do. For category= in your second query to work, you would need to give a single element and not a queryset. Replace your filter with get in the first query and it might work fine.
Or
If you think that popular_category_id can have more than one row for the category popular, leave the first query as it is and change your second query to
popular_courses_ids = CourseTrack.objects.values('course_id').filter(category__in=popular_category_id).order_by('tracks')
I have changed category to category__in.

Limit values in the modelformset field

I've been trying to solve this problem for a couple of days now, getting quite desperate. See the commented out code snippets for some of the things I've tried but didn't work.
Problem: How can I limit the values in the category field of the IngredientForm to only those belonging to the currently logged in user?
views.py
#login_required
def apphome(request):
IngrFormSet = modelformset_factory(Ingredient, extra=1, fields=('name', 'category'))
# Attempt #1 (not working; error: 'IngredientFormFormSet' object has no attribute 'fields')
# ingrformset = IngrFormSet(prefix='ingr', queryset=Ingredient.objects.none())
# ingrformset.fields['category'].queryset = Category.objects.filter(user=request.user)
# Attempt #2 (doesn't work)
# ingrformset = IngrFormSet(prefix='ingr', queryset=Ingredient.objects.filter(category__user_id = request.user.id))
models.py:
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
class Ingredient(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(Category, null=True, blank=True)
counter = models.IntegerField(default=0)
forms.py:
class IngredientForm(ModelForm):
class Meta:
model = Ingredient
fields = ('name', 'category')
UPDATE: I've made some progress but the solution is currently hard-coded and not really usable:
I found out I can control the categoryform field via form class and then pass the form in the view like this:
#forms.py
class IngredientForm(ModelForm):
category = forms.ModelChoiceField(queryset = Category.objects.filter(user_id = 1))
class Meta:
model = Ingredient
fields = ('name', 'category')
#views.py
IngrFormSet = modelformset_factory(Ingredient, form = IngredientForm, extra=1, fields=('name', 'category'))
The above produces the result I need but obviously the user is hardcoded. I need it to be dynamic (i.e. current user). I tried some solutions for accessing the request.user in forms.py but those didn't work.
Any ideas how to move forward?
You don't need any kind of custom forms. You can change the queryset of category field as:
IngrFormSet = modelformset_factory(Ingredient, extra=1, fields=('name', 'category'))
IngrFormSet.form.base_fields['category'].queryset = Category.objects.filter(user__id=request.user.id)
Category.objects.filter(user=request.user)
returns a list object for the initial value in your form which makes little sense.
Try instead
Category.objects.get(user=request.user)
or
Category.objects.filter(user=request.user)[0]