Django join fields in queryset - django

Is it possible to join common fields in different queryset. I have day_name = Monday in two queryset. I want to combine it into one. The reason i wanted to do it because when mapping it on frontend react. I want to see only one Monday , not two mondays.
[
{
"day__day_name": "Monday",
"subject_name__subject_name": "Maths"
},
{
"day__day_name": "Monday",
"subject_name__subject_name": "English"
}
]
I want to join these both "Monday" so that i can only return it as one "Monday" instead of two or more
class SubjectList(models.Model):
subject_name = models.CharField(max_length=25)
def __str__(self):
return self.subject_name
class Day(models.Model):
day_name = models.CharField(max_length=15)
subject_name = models.ManyToManyField(SubjectList, through='time_table')
def __str__(self):
return self.day_name
class time_table(models.Model):
class_number = models.ForeignKey(
AddClass, on_delete=models.CASCADE, null=True, blank=True
)
day = models.ForeignKey(Day, on_delete=models.CASCADE)
subject_name = models.ForeignKey(SubjectList, on_delete=models.CASCADE)
begin = models.TimeField(null=True)
end = models.TimeField(null=True)
def __str__(self):
return str(self.class_number)
#Views
class New_time_table(APIView):
def get(self, request, id):
cls = AddClass.objects.get(id=id)
tt = (
time_table.objects.select_related("day")
.filter(class_number=cls.id)
.values("day__day_name", "subject_name__subject_name")
.distinct()
)
return Response(tt)

This Django query can help you.
Model.objects.select_related('day').values('day__day_name','subject_name__subject_name').order_by('day__day_name').distinct()

Related

Filter by fields from foreignKey relationships

I got a bunch of models and some of them are connected (by foreign-key relationships) and I wrote a serializer which allows me to print out all of the connected fields that I want, and leave out what I do not want to see. Great. Now I also have a basic filter, which uses the model (PmP) which contains all the foreignkeys, but now I want to add another filter for a field (field name e from PmPr Model) from a different Model, one that is read in via foreignkey connection (li in Model PmP connects to model PmL containing field pro which connects to model PmPr where the field e is). But I dont know how to do that and as far as I can see, I cant set two filter_classes inside my view (PmPLListView)?! And I dont know how to access the field via the foreignkey relation. So how do I go about this? If I can access the e field from PmPr Model via my existing filter - than that is also fine with me, I dont necessary want two filter classes (if even possible). It was just me first thought. (btw. sorry about the strange names, but unfortunately I'm not allowed to write the real names)
these are my models (at least the relevant ones):
class PmP(models.Model):
created_at = models.DateTimeField()
pr = models.DecimalField(max_digits=6, decimal_places=2)
li = models.ForeignKey(PmL, models.DO_NOTHING)
se = models.ForeignKey('PmSe', models.DO_NOTHING)
class Meta:
managed = False
db_table = 'pm_p'
class PmL(models.Model):
u = models.TextField()
pro = models.ForeignKey('PmPr', models.DO_NOTHING)
sh = models.ForeignKey('PmS', models.DO_NOTHING)
active = models.IntegerField()
class Meta:
managed = False
db_table = 'pm_l'
class PmSe(models.Model):
name = models.TextField()
s_i_id = models.TextField(blank=True, null=True)
sh = models.ForeignKey('PmS',
models.DO_NOTHING,
blank=True,
null=True)
class Meta:
managed = False
db_table = 'pm_se'
class PmPr(models.Model):
name = models.TextField()
e = models.CharField(max_length=13)
created_at = models.DateTimeField()
cus = models.ForeignKey(PmC, models.DO_NOTHING)
u_v_p = models.DecimalField(max_digits=10,
decimal_places=2,
blank=True,
null=True)
cf = models.IntegerField(blank=True, null=True)
s_k_u = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'pm_pr'
this is what my serializer looks like:
class PmPLSerializer(serializers.ModelSerializer):
# id = serializers.SerializerMethodField('get_l_id')
u = serializers.SerializerMethodField('get_l_u')
sh = serializers.SerializerMethodField('get_sh_name')
name = serializers.SerializerMethodField('get_pro_name')
e = serializers.SerializerMethodField('get_pro_e')
u_v_p = serializers.SerializerMethodField('get_pro_u_v_p')
s_k_u = serializers.SerializerMethodField('get_pro_s_k_u')
se = serializers.SerializerMethodField('get_se_name')
pr = serializers.SerializerMethodField('get_pr')
created_at = serializers.SerializerMethodField('get_created_at')
class Meta:
model = PmP
# fields = '__all__'
fields = ('u', 'sh', 'name', 'e', 's_k_u', 'u_v_p', 'pr',
'created_at', 'se')
depth = 2
def get_l_id(self, obj):
return obj.li.id
def get_l_u(self, obj):
return obj.li.u
def get_sh_name(self, obj):
return obj.li.sh.name
def get_pro_name(self, obj):
return obj.li.pro.name
def get_pro_e(self, obj):
return obj.li.pro.e
def get_pro_u_v_p(self, obj):
return obj.li.pro.u_v_p
def get_pro_s_k_u(self, obj):
return obj.li.pro.s_k_u
def get_se_name(self, obj):
return obj.se.name
def get_pr(self, obj):
return obj.pr
def get_created_at(self, obj):
return obj.created_at
this is my filter class:
class PmPFilter(rfilters.FilterSet):
class Meta:
model = PmP
fields = [
"created_at",
"pr",
]
for field in ["pr"]:
exec(f'min_{field} = rfilters.NumberFilter(field, lookup_expr="gte")')
exec(f'max_{field} = rfilters.NumberFilter(field, lookup_expr="lte")')
# filter by date as "is_less_than_or_equal_to"
written_to = rfilters.CharFilter(method="created_at_to", label="created_at to")
# filter by date as "is_greater_than_or_equal_to"
written_from = rfilters.CharFilter(method="created_at_from", label="created_at from")
# filter by exact date
written = rfilters.CharFilter(method="created_at_exact", label="created_at exact")
def created_at_exact(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at=cdate)
def created_at_to(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at__lte=cdate)
def created_at_from(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at__gte=cdate)
def parse_date(self, value):
return (
parser.parse(value).year,
parser.parse(value).month,
parser.parse(value).day,
parser.parse(value).hour,
parser.parse(value).minute,
parser.parse(value).second,
)
and finally, this is my view:
class PmPLListView(generics.ListAPIView):
queryset = PmP.objects.all()
serializer_class = PmPLSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ["created_at", "pr"]
filter_class = PmPFilter
fields = ("created_at", "pr")
filter_fields = fields
search_fields = fields
def get_queryset(self):
"""
This view should return a list of all data
"""
return PmP.objects.filter()
oh I got it! I can access the foreign relationship with two underscores. So I modified my Filter class to this:
class PmPFilter(rfilters.FilterSet):
class Meta:
model = PmPrice
fields = [
"created_at",
"pr",
"li__pro__e",
]
...
and inside my PmPLListView view I also added the double underscores to access the field:
class PmPLListView(generics.ListAPIView):
queryset = PmP.objects.all()
serializer_class = PmPLSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ["created_at", "pr"]
filter_class = PmPFilter
fields = ("created_at", "pr", "li__pro__e")
filter_fields = fields
search_fields = fields
now I can filter by field e

How can I show the StringRelatedField instead of the Primary Key while still being able to write-to that field using Django Rest Framework?

Models:
class CrewMember(models.Model):
DEPARTMENT_CHOICES = [
("deck", "Deck"),
("engineering", "Engineering"),
("interior", "Interior")
]
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.EmailField()
department = models.CharField(max_length=12, choices=DEPARTMENT_CHOICES)
date_of_birth = models.DateField()
join_date = models.DateField()
return_date = models.DateField(null=True, blank=True)
leave_date = models.DateField(null=True, blank=True)
avatar = models.ImageField(null=True, blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
class RosterInstance(models.Model):
date = models.DateField(default=timezone.now)
deckhand_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="deckhand_watches")
night_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="night_watches")
def __str__(self):
return self.date.strftime("%d %b, %Y")
Views:
class CrewMemberViewSet(viewsets.ModelViewSet):
queryset = CrewMember.objects.all()
serializer_class = CrewMemberSerializer
filter_backends = [SearchFilter]
search_fields = ["department"]
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.active = False
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class RosterInstanceViewSet(viewsets.ModelViewSet):
queryset = RosterInstance.objects.all()
serializer_class = RosterInstanceSerializer
Serializers:
class CrewMemberSerializer(serializers.ModelSerializer):
class Meta:
model = CrewMember
fields = "__all__"
class RosterInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = RosterInstance
fields = "__all__"
The resulting data looks like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": 1,
"night_watchkeeper": 3
}
But I want it to look like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": "Joe Soap",
"night_watchkeeper": "John Smith"
}
I can achieve the above output by using StringRelatedField in the RosterInstanceSerializer but then I can no longer add more instances to the RosterInstance model (I believe that is because StringRelatedField is read-only).
Because StringRelaredField is always read_only, you can use SlugRelatedField instead:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = serializers.SlugRelatedField(
slug_field='deckhand_watchkeeper'
)
night_watchkeeper = serializers.SlugRelatedField(
slug_field='night_watchkeeper'
)
class Meta:
model = RosterInstance
fields = ['id', 'date', 'deckhand_watchkeeper', 'night_watchkeeper']
I was created a WritableStringRelatedField to do that.
class WritableStringRelatedField(serializers.SlugRelatedField):
def __init__(self, display_field=None, *args, **kwargs):
self.display_field = display_field
# Set what attribute to be represented.
# If `None`, use `Model.__str__()` .
super().__init__(*args, **kwargs)
def to_representation(self, obj):
# This function controls how to representation field.
if self.display_field:
return getattr(obj, self.display_field)
return str(obj)
def slug_representation(self, obj):
# It will be called by `get_choices()`.
return getattr(obj, self.slug_field)
def get_choices(self, cutoff=None):
queryset = self.get_queryset()
if queryset is None:
# Ensure that field.choices returns something sensible
# even when accessed with a read-only field.
return {}
if cutoff is not None:
queryset = queryset[:cutoff]
return OrderedDict([
(
self.slug_representation(item),
# Only this line has been overridden,
# the others are the same as `super().get_choices()`.
self.display_value(item)
)
for item in queryset
])
Serializers:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Deckhand Watchkeeper',
)
night_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Night Watchkeeper',
)
class Meta:
model = RosterInstance
fields = "__all__"

Django category view

I am new to django and maybe this is a stupid question but i got stuck with this for a while now.. so i have a few categories of meds, like AINS, antidepressants and each of this category has its own meds, and i am trying to show my users all the meds of a specific category: so if a users types in www.namesite.com/meds/AINS the it will show only the meds for that specific category .. AINS.I think that i should get the absolute url of every category and filter all the meds in that specific category?
Model:
class Category(models.Model):
category = models.CharField(max_length=30)
slug = models.SlugField()
def __str__(self):
return self.category
def get_absolute_url(self):
return reverse("meds", kwargs={'slug':self.category})
class Meta:
verbose_name_plural = 'Categorii'
class Medicament(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(max_length=200)
category = models.ForeignKey(Category, on_delete='CASCADE')
price = models.DecimalField(decimal_places=2, max_digits=4)
prospect = models.TextField(default='Prospect')
company = models.TextField(default = 'company')
nr_unitati = models.IntegerField()
quantity = models.CharField(max_length=5, default='mg')
date_added = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(null=True, blank=True)
amount = models.IntegerField(default=0)
def __str__(self):
return self.title + ' ' + self.company + ' ' + str(self.nr_unitati) + ' ' + self.quantity
class Meta:
verbose_name_plural = 'Medicamente'
Views:
class MedCategoriesView(DetailView):
model = Category
template_name = 'products/AINS.html'
context_object_name = 'all_categories'
def get_context_data(self, **kwargs):
context = super(AINS_ListView, self).get_context_data(**kwargs)
context['meds'] = Medicament.objects.filter(category=self.object)
return context
Urls:
path('medicaments/<slug>/', MedCategoriesView.as_view(), name='meds'),
Using function based views.
def medicament(request, slug):
try:
medicaments = Medicament.objects.filter(category__slug=slug)
except Medicament.DoesNotExist:
raise Http404("Medicament does not exist")
return render(request, 'products/AINS.html', {'medicaments': medicaments})

filter list of objects by filtering a list of objects

I have two models ItemCategory and Item, I want to filter the list of ItemCategory by filtering is_published field of item.
class ItemCategory(models.Model):
category_name = models.CharField(max_length=50, unique=True)
category_image = models.ImageField(upload_to='item-category', null=True)
def __str__(self):
return 'category: ' + self.category_name
class Item(models.Model):
item_name = models.CharField(max_length=50)
item_desc = models.CharField(max_length=500, blank=True)
price = models.FloatField()
item_image = models.ImageField(upload_to='item-images')
num_of_items_available = models.IntegerField()
category_name = models.ForeignKey(ItemCategory, on_delete=models.CASCADE, null=True, related_name='items')
is_published = models.BooleanField(default=False)
def __str__(self):
return 'item: ' + self.item_name
Here is my approach but didn't get any success.
class ItemCategoryView(viewsets.ViewSet):
permission_classes = (AllowAny,)
serializer_class = ItemCategoryListSerializer
def list(self, request, format=None):
queryset = ItemCategory.objects.filter(items__in=Item.objects.filter(is_published=True))
serializer = ItemCategorySerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
ItemCategorySerializer looks like this
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('pk', 'item_name', 'item_desc', 'price', 'item_image', 'num_of_items_available',
'category_name', 'is_published')
class ItemCategorySerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = ItemCategory
fields = ('pk', 'category_name', 'category_image', 'items')
Can anyone tell my how i can approach to this problem?
Edit
I have tried this two queries
queryset = ItemCategory.objects.filter(items__in=Item.objects.filter(is_published=True)) queryset = ItemCategory.objects.filter(items__is_published=True)
I think both this query work but not in the way i want. Heres how it works now.
It returns list of ItemCategory where any list of item with is_published = True. Say There are 3 categories(A, B , C) and each category has 3 list of item (A1, A2, A3, B1, B2, B3, C1, C2, C3). and let all have is_published = False except C3.
So now the query result is something like this. It returns only C Category with all the list item C1, C2, C3. which is not the desired result. I should be getting only C3 since this is the only published Item.
You can achieve this with filter and prefetch_related:
queryset = ItemCategory.objects.filter(items__is_published=True).prefet‌​ch_related(Prefetch(‌​"items", queryset=Item.objects.fiter(is_published=True))
I think you need to filter them items before return the the category itself.
Filter all published items.
Filter all categories by published items.
Maybe you can use this method, but previously I suggest you to use the QuerySet manager.
1. models.py
class ItemCategory(models.Model):
category_name = models.CharField(max_length=50, unique=True)
category_image = models.ImageField(upload_to='item-category', null=True)
def get_published_items(self):
"""
return all items contains with this single Category.
"""
return Item.objects.published().filter(category_name__pk=self.pk)
def __str__(self):
return 'category: ' + self.category_name
class ItemQuerySet(models.QuerySet):
def published(self):
return self.filter(is_published=True)
def unpublished(self):
return self.filter(is_published=False)
class Item(models.Model):
....
category_name = models.ForeignKey(
ItemCategory, on_delete=models.CASCADE,
null=True, related_name='items')
is_published = models.BooleanField(default=False)
objects = ItemQuerySet.as_manager()
def __str__(self):
return 'item: ' + self.item_name
2. views.py
class ItemCategoryView(viewsets.ViewSet):
permission_classes = (AllowAny,)
serializer_class = ItemCategoryListSerializer
def list(self, request, format=None):
# find all categories first
published_categories_by_items = [ c.category_name for c in Item.objects.published() ]
# then, filter the categories itself.
queryset = ItemCategory.objects.filter(pk__in=[ c.pk for c in published_categories_by_items]).distinct()
serializer = ItemCategorySerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

Subtract models.DateField to get number of days

I have a simple model that tracks work leave requests:
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
In the view I need a function to calculate the number of days requested. Secondarily, I'll need a method to count only weekdays, but for now I've got the following:
def leave_screen(request, id):
records = LeaveRequest.objects.filter(employee=id)
total_days = LeaveRequest.return_date - LeaveRequest.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
which produces a attribute error
type object 'LeaveRequest' has no attribute 'return_date
any suggestions?
In total_days, you are calling the model and not the instance of that model - records - that you created.
If you want to view just a single Leave record, you would need to pass the id of the LeaveRequest
def leave_screen(request, id):
records = LeaveRequest.objects.get(id=id)
total_days = records.return_date - records.start_date
tpl = 'vacation/leave_request.html'
return render_to_response(tpl, {'records': records })
The answer that suggests using it as a property will work but I think I'll prefer keeping it as a field and just computing it at the time of insert.
class LeaveRequest(models.Model):
employee = models.ForeignKey(UserProfile)
supervisor = models.ForeignKey(UserProfile, related_name='+', blank=False, null=False)
submit_date = models.DateField(("Date"), default=datetime.date.today)
leave_type = models.CharField(max_length=64, choices=TYPE_CHOICES)
start_date = models.DateField(("Date"))
return_date = models.DateField(("Date"))
total_days = models.IntegerField()
notes = models.TextField(max_length=1000)
def __unicode__ (self):
return u'%s %s' % (self.employee, self.submit_date)
def save(self, *args, **kwargs):
self.total_days = (self.return_date - self.start_date).days
super(LeaveRequest, self).save(*args, **kwargs)
class Admin:
pass
class Meta:
ordering = ['-submit_date']
This way when you put in the logic for excluding weekends you are saving computation to calculate the days everytime at the time of listing all leave requests.
I wouldn't have 'total_days' as a field in the LeaveRequest class, but rather as a property.
class LeaveRequest(models.Model):
(other fields)
#property
def total_days(self):
oneday = datetime.timedelta(days=1)
dt = self.start_date
total_days = 0
while(dt <= self.return_date):
if not dt.isoweekday() in (6, 7):
total_days += 1
dt += oneday
return totaldays
# view function
def leave_screen(request, id):
# get leave request by id
leavereq = LeaveRequest.objects.get(id=id)
return render_to_response("vacation/leave_request.html", {"leavereq": leavereq})
# template code
...
<body>
{{ leavereq.total_days }}
</body>