I want a listing to be able to have a specific category, so the seller will choose a general category and then onto a more specific one, for example:
Men's Wear:
Jeans:
Skinny Fit
Slim Fit
Regular Fit
Pants:
Slim Fit
Formal
Casual
Women's Apparel:
Tops:
Tunics
Denim:
Jeans
I tried the named groups like in django docs:
https://docs.djangoproject.com/en/3.0/ref/models/fields/#field-choices-named-groups
MEDIA_CHOICES = [
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
]
But when I tried, it gives the error
product.Listing.category: (fields.E005) 'choices' must be an iterable containing (actual value, human readable name) tuples.
here is my code:
class Listing(models.Model):
CATEGORY = [
('Women\'s Apparel', (
('Dresses', (
('party_dresses', 'Party Dresses'),
)
),
),
),
]
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
new_condition = models.BooleanField()
category = models.CharField(choices=CATEGORY, max_length=255)
why is it showing error ? is it because the enum groups cant be nested more than 1 level ?
thank you
Related
I am trying to annotate all my groups by the number of Users, that apply to a certain condition. In this case I want to get a number of users, that have related Offer model to the Task in the Group.
Users can create Offers to the Task. One Task can be related to one Group.
So as the result of annotating Groups I want something like this
| id | name | runners_num |
| -- | ------ | ----------- |
| 1 | name1 | 3 |
| 2 | name2 | 5 |
With a query below I can get this count as a dictionary
runners = User.objects.filter(
offer__tack__group_id=1
).distinct(
).values(
'id'
).aggregate(
count=Count('id')
)
output: {'count': 5}
But I can't figure out how to do this with OuterRef clause
runners = User.objects.filter(
offer__task__group_id=OuterRef('id')
).distinct().values('id')
groups = Group.objects.annotate(
runners_num=Count(Subquery(runners))
)
It ended up with this wrong query
SELECT
"groups"."id",
"groups"."name",
COUNT(
(
SELECT
DISTINCT U0."id"
FROM
"users" U0
INNER JOIN "offers" U1 ON (U0."id" = U1."runner_id")
INNER JOIN "tasks" U2 ON (U1."task_id" = U2."id")
WHERE
U2."group_id" = ("groups"."id")
)
) AS "runners_num"
FROM
"groups"
GROUP BY
"groups"."id"
LIMIT
21
My models
class Task(models.Model):
tasker = models.ForeignKey(
"user.User", null=True, blank=True, on_delete=models.SET_NULL, related_name="tasker"
runner = models.ForeignKey(
"user.User", null=True, blank=True, on_delete=models.SET_NULL, related_name="runner",
)
group = models.ForeignKey(
"group.Group",
on_delete=models.CASCADE
)
class Offer(models.Model):
task = models.ForeignKey("task.Task", on_delete=models.CASCADE)
runner = models.ForeignKey("user.User", null=True, blank=True, on_delete=models.SET_NULL)
class Group(model.Model):
name = models.CharField(max_length=100)
class GroupMembers(models.Model):
group = models.ForeignKey("group.Group", on_delete=models.CASCADE)
member = models.ForeignKey("user.User", null=True, blank=True, on_delete=models.SET_NULL)
EDIT
I have conditions where my Users get filtered. In my case I want count only Users that have more than 3 Offers that apply to conditions. So probably I can't get rid of Subquery statement and OuterRef field.
runners = User.objects.filter(
offer__task__group_id=OuterRef('id')
).distinct(
).annotate(
offer_num=Count(
'offer',
filter=
Q(
offer__task__completion_time__isnull=False,
offer__task__completion_time__gte=timezone.now() - timedelta(hours=24),
) |
Q(
offer__task__status__in=(
TaskStatus.ACTIVE,
TaskStatus.ACCEPTED,
TaskStatus.IN_PROGRESS,
TaskStatus.WAITING_REVIEW
),
offer__task__is_active=True,
),
offer__runner_id=F('id'),
)
).filter(
offer_num__gte=3
).values('id')
It is working fine if I replace OuterRef('id') with just a int Group number. But I don't know the proper solution how to count on this QuerySet. Something like
runners_num = Count(Subquery(runners))
And after that I get
django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression
Annotate the number of users who have at least one offer for each group's task, correct?
I believe this should do:
Group.objects.annotate(
runners_count=Count(
'task__offer__user', distinct=True
)
).values('id', 'runners_count')
Count() has some neat kwargs you can leverage like that: https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.Count
So my final solution is to carry out User's subquery and only then aggregate by Count function.
users_runners = User.objects.filter(
offer__task__group_id=OuterRef(OuterRef('id'))
).annotate(
offer_num=Count(
'offer',
filter=
Q(
offer__task__completion_time__isnull=False,
offer__task__completion_time__gte=timezone.now() - timedelta(hours=24),
) |
Q(
offer__task__status__in=(
TaskStatus.ACTIVE,
TaskStatus.ACCEPTED,
TaskStatus.IN_PROGRESS,
TaskStatus.WAITING_REVIEW
),
offer__task__is_active=True,
)
)
).filter(
offer_num__gte=3
).values(
'id'
).distinct(
)
runners = User.objects.filter(
id__in=users_runners,
).annotate(
count=Func(
F('id'),
function='Count'
)
).values(
'count'
)
groups = Group.objects.annotate(
runners_num=runners
).order_by(
'id'
)
Let's say I'm making a media library model, and there are these choices in named groups:
models.py
class MediaLibrary(models.Model):
MEDIA_CHOICES = [
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
)
]
media_type = models.CharField(max_length=50, choices=Media_Choices)
If I make a request through DRF, it would return all the objects which match either "Audio" and "Video".
How can I filter the request so that it only returns objects under the "Audio" group. (Or the "Video" group.)
create a model manger and do filter as follows
class MediaManager(models.Manager):
def video_items(self):
return self.get_queryset().filter(media_type='Video')
---your models here--
objects = MediaManager()
then you can call in your views as MediaLibrary.objects.video_items(), similarly filer audio too.
I'm fairly new to Django and I want to get the total clients a user has (my users will be selling something through my website) so I have a table called orders where I keep the user_id of the user who purchased and the product_id being purchased. Each product id is related to a product that has a user (the one I'm doing the query for):
select COUNT(distinct(o.user_id)) as total_clients from `order` o
inner join product p on p.id=o.product_id where p.user_id=32;
User id 32 is logged in and I want to show him how many clients purchased his products.
I want to do this in a get instead of filter as it makes more sense.
Here's what my logic tells me to write:
clients = Order.objects.get(
status = Order.COMPLETED,
product__user = self.user
).annotate(
total_clients = Count( 'user', distinct = True )
)
return clients.total_clients
.and here's what it returns:
Exception Type: MultipleObjectsReturned
Exception Value: get() returned more than one Order -- it returned 2!
I could probably be running a query instead of using the orm but I don't want that. This is a rather simple query that I'm sure Django is handling very easily and I want to keep away from writing strings in my code.
Here's my model setup:
class UserProfile( models.Model ):
user = models.OneToOneField( User, related_name = 'profile' )
....
def get_total_clients( self ):
clients = Order.objects.get(
status = Order.COMPLETED,
product__user = self.user
).annotate(
total_clients = Count( 'user', distinct = True )
)
return clients.total_clients
class Order( models.Model ):
PENDING = 0
COMPLETED = 1
REFUNDED = 2
STATUS_CHOICES = (
(PENDING, "Pending"),
(COMPLETED, "Completed"),
(REFUNDED, "Refunded"),
)
user = models.ForeignKey( User, related_name = "orders" )
product = models.ForeignKey( Product, related_name = "orders" )
amount = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0.00 )
status = models.SmallIntegerField(
choices = STATUS_CHOICES, default = PENDING, db_index = True
)
class Product( models.Model ):
user = models.ForeignKey( User, related_name = "unlocks" )
name = models.CharField( max_length = 255 )
Django queryset have a count method:
clients = Order.objects.filter(
status = Order.COMPLETED,
product__user = self.user
)
return clients.count()
If i got you right, you are intrested in the amount of consumers who ordered a product from a user. Some docs, that may be helpful.
My suggestion is:
result = Product.objects.distinct().filter(
# or any query:
orders__status=Order.COMPLETED,
user=default_user
).aggregate( # see the difference between aggregate() and annotate()
total_users=Count('orders__user', distinct=True)
)
I expect the result to be: {'total_users': NUM_OF_USERS}
In raw SQL it will be something like:
SELECT DISTINCT COUNT(DISTINCT "main_order"."user_id") AS
"total_users" FROM "main_product" INNER JOIN "main_order" ON (
"main_product"."id" = "main_order"."product_id" ) WHERE
("main_order"."status" = STATUS AND "main_product"."user_id" = USER_ID)
Is that what you wanted?
I have a choice tuple like that
CATEGORY_CHOICES = (
(DB_ENUMS.GENERAL.EMPTY, 'All'),
('Fashion and style',
(
(DB_ENUMS.CATEGORY.FASHION_AND_STYLE, 'All Fashion and Style'),
('Clothes/Shoes',
(
(1, 'string')
)
)
)
)
)
But this one
((1, 'string'))
is shown on the same level as
'All Fashion and Style'
Am I trying to use forbidden things? I found nothing about limits in documentation.
By default, Django only supports 2 levels of hierarchy in a choice field:
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
)
However, there are some third-party apps like django-categories that provide the ability to have a hierarchy of choices.
I have a grouped category field. The problem is that I've created a search form, but when I try presente the form to the user in the template, it goes wrong.
models.py
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
)
category = models.CharField(max_length=20, choices=MEDIA_CHOICES, verbose_name=_(u'Category'))
forms.py (search)
class SearchingForm(forms.Form):
"Search Box"
search = forms.CharField(max_length=100, required=False, label=(_(u'Search')))
music_kind = forms.MultipleChoiceField(choices=MEDIA_CHOICES, required=False,
label=(_(u'Kind')),
widget=forms.CheckboxSelectMultiple(),
)
template.html
{{ form.search }}
{{ form.place_kind }}
I show the form to the user like this, the problem is when I rendered with a browser I have something like this (in each line, it has a checkbox):
(('vinyl', 'Vinyl'), ('cd', 'CD'))
(('vhs', 'VHS Tape'), ('dvd', 'DVD'))
Unknown
I have delete the 'widget=forms.CheckboxSelectMultiple()' attribute it goes right, but I don't have a checkboxes. So, How I can do it with checkbox fields?
I think you have a data type mismatch here. You're wanting to store multiple values in a single CharField. Sure, you could save a dictionary of key-value pairs in there, but then you'd have to parse it back out into selections, and that's a huge pain.
I would move your MEDIA_CHOICES to a database table, and then in your SearchingForm, you can do a CheckboxSelectMultiple, and the form will behave as expected.
I'm not sure, but I wonder if choice groups are only for select boxes (not checkboxes).