if i inserting the number 5 i want to check all the numbers before 5( like 1,2,3 and 4 )is must have inside the db ,then only can i add the number 5 ,otherwise show error. And also check is any duplication in the Db using rest django model serilaizer.
def validate_match_round(self, match_round):
if not match_round :
raise serializers.ValidationError("Enter value more than 0 ")
matchscore model
class Matchscore(TimeStampedModel):
gameevent = models.ForeignKey(GameEvent, null=True, related_name='game_event',on_delete=models.DO_NOTHING)
match_round = models.IntegerField(null=True,blank=True)
team_a = models.ForeignKey(Team,null=True,related_name='team_one',on_delete=models.DO_NOTHING)
team_a_score = models.PositiveIntegerField(null=True,blank=True)
team_b = models.ForeignKey(Team,null=True,related_name='team_two',on_delete=models.DO_NOTHING)
team_b_score = models.PositiveIntegerField(null=True,blank=True)
team_won = models.ForeignKey(Team,null=True,related_name='team', on_delete=models.DO_NOTHING)
if match_round == 1:
return match_round
match = Matchscore.objects.aggregate(Max('match_round'))
print(match)
if match_round == match["match_round__max"] + 1 :
return match_round
else:
raise serializers.ValidationError("Enter a valid match_round")
all_rounds = Matchscore.objects.filter(match_round__lte=match_round).order_by('match_round').values_list('match_round', flat=True)
Will return list of match_round values that lower or equal than match_round passed to function
all_rounds_count = Matchscore.objects.filter(match_round__lte=match_round).count()
Will return count of these rounds. and if (all_rounds_count + 1) == match_round check is what you want
And also check is any duplication in the Db using rest django model serilaizer.
To avoid keys duplication
Set unique=True for model.Field to disable round duplicates match_round = models.IntegerField(..., unique = True)
Add unique validator to serializer
Related
I have a Item model with a numeric number field. This number field defaults to null.
# models.py
class Item(models.Model):
number = models.IntegerField(default=None, blank=True, null=True)
I want to set-up filters that can return a queryset of Items where number is in range - which is straightforward enough:
# filters.py
class ItemFilter(django_filters.FilterSet):
min_num = django_filters.NumberFilter(method="min_num_filter")
max_num = django_filters.NumberFilter(method="max_num_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
def min_num_filter(self, queryset, name, value):
return queryset.filter(number__gte=value)
def max_num_filter(self, queryset, name, value):
return queryset.filter(number__lte=value)
But what if I want to have an additional Boolean filter that can include Items that has null for number along with whatever Items matches the min_num and max_num range?
So for example, a URL query in the form of ?min_num=1&max_num=10&incl_null=True should return all Items where number is between 1 and 10 OR number is equal to None.
The following code does not work:
class ItemFilter(django_filters.FilterSet):
...
incl_null = django_filters.BooleanFilter(method="incl_null_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
// doesn't work
class incl_null_filter(self, queryset, name, value):
if value is True:
return queryset | Item.objects.filter(number=None)
if value is False:
return queryset
Edit: I've tried the methods in the "Filtering by empty values" documentation but I think that's for null values exclusively - where I'm looking for a range match OR a null value.
Try this query:
from django.db.models import Q
min_ = 0
max_ = 10
Item.objects.filter(Q(number__gte=min_, number__lte=max_) | Q(number__isnull=True))
Well, the only solution I can think of is to pass the min range, max range, and is_null boolean into a single char field then convert it into the 3 individual filters for actioning.
So the query URL will look like ?master_num=1-10-1 for range 1 - 10 incl. None and ?master_num=1-10-0 for range 1 - 10 excl. None.
class ItemFilter(django_filters.FilterSet):
master_num = django_filters.CharFilter(method="master_num_filter")
class Meta:
model = Item
fields = ("master_num")
def master_num_filter(self, queryset, name, value):
# array = [min, max, 1 or 0 for True and False]
array = value.split("-")
min = Q(year_published__gte=int(array[0]))
max = Q(year_published__lte=int(array[1]))
if array[2] == "1":
incl_null = Q(year_published=None)
return queryset.filter((min & max) | incl_null)
else:
return queryset.filter(min & max)
Would like to know if there's a better way to do this.
I am trying to generate a result that satisfies with the filter query below:
indicators = request.GET.getlist('indicators[]')
fmrprofiles = FMRPriority.objects.all()
q_objects = Q()
obj_filters = []
for indicator in indicators:
split_i = indicator.split('_')
if len(split_i) == 5:
if not any(d['indicator'] == split_i[1] for d in obj_filters):
obj_filters.append({
'indicator': split_i[1],
'scores': []
})
for o in obj_filters:
if split_i[1] == o['indicator']:
o['scores'].append(int(split_i[4]))
for obj in obj_filters:
print (obj['scores'])
q_objects.add(Q(pcindicator__id = int(obj['indicator'])) & Q(score__in=obj['scores']), Q.AND)
print (q_objects)
fmrprofiles = fmrprofiles.values('fmr__id','fmr__road_name').filter(q_objects).order_by('-fmr__date_validated')
print (fmrprofiles.query)
Basically, indicators is a list e.g. ['indicator_1_scoring_1_5', 'indicator_1_scoring_1_4', 'indicator_2_scoring_2_5']
I wanted to filter FMRPriority with these following fields:
pcindicator
score
e.g. pcindicator is equal 1 and scores selected are 5,4..another selection pcindicator is equal to 2 and scores selected are 3.
The query q_objects.add(Q(pcindicator__id = int(obj['indicator'])) & Q(score__in=obj['scores']), Q.AND) returns empty set..i have tried also the raw sql, same result.
Model:
class FMRPriority(models.Model):
fmr = models.ForeignKey(FMRProfile, verbose_name=_("FMR Project"), on_delete=models.CASCADE)
pcindicator = models.ForeignKey(PCIndicator, verbose_name=_("Priority Indicator"), on_delete=models.PROTECT)
score = models.FloatField(_("Score"))
I solve this by using OR and count the occurrence of id then exclude those are not equal to the length of filters:
for obj in obj_filters:
print (obj['scores'])
q_objects.add(
(Q(fmrpriority__pcindicator__id = int(obj['indicator'])) & Q(fmrpriority__score__in=obj['scores'])), Q.OR
)
fmrprofiles = fmrprofiles.values(*vals_to_display).filter(q_objects).annotate(
num_ins=Count('id'),
...
)).exclude(
~Q(num_ins = len(obj_filters))
).order_by('rank','road_name')
I got the following code, that contains N queries:
for qty in total_qty_bought:
product_id = qty["product"]
quantity = int(qty["quantity__sum"])
try:
method_title = (
self.shipment_model.get(order_id=qty["order_id"])
.method_title.replace("Hent-selv", "")
.strip()
)
To solve the issue I tried to take the method_title query out of the for loop like this:
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id'])
.method_title.replace("Hent-selv", "")
.strip()
)
Note! There will be a full refrence further down, to understand the bigger picture
The issue in my solution is, that I am hard choosing which dict to enter , as I select [0] before order_id, and not in a for loop like before, would be selecting every individual item in the loop.
Is there a more sufficient way to do this? I do not see a solution without the for loop, but django debugger tool tells me it creates 2k+ queries.
CODE FOR REFRENCE
class InventoryStatusView(LoginRequiredMixin, View):
template_name = "lager-status.html"
cinnamon_form = CinnamonForm(prefix="cinnamon_form")
peber_form = PeberForm(prefix="peber_form")
pc_model = InventoryStatus
product_model = Product.objects.all()
order_item_model = WCOrderItem.objects.all()
shipment_model = WCOrderShipment.objects.all()
def get(self, request):
# Get all added objects that hasn't been deleted
objects = self.pc_model.objects.filter(is_deleted=False)
# Get all added objects that has been deleted
deleted_objects = self.pc_model.objects.filter(is_deleted=True)
# Sum all cinnamon that isn't deleted
total_cinnamon = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("cinnamon"))
.get("cinnamon__sum", 0.00)
)
# Sum all peber that isn't deleted
total_peber = (
self.pc_model.objects.filter(is_deleted=False)
.aggregate(Sum("peber"))
.get("peber__sum", 0.00)
)
# Get the amount of kilo attached to products
product_data = {}
queryset = ProductSpy.objects.select_related('product')
for productSpy in queryset:
product_data[productSpy.product.product_id] = productSpy.kilo
# Get quantity bought of each product
total_qty_bought = self.order_item_model.values(
"order_id", "product"
).annotate(Sum("quantity"))
# Get the cities from the inventory model
cities = dict(self.pc_model.CITIES)
# Set our total dict for later reference
our_total = {}
product = Product.objects.filter(
product_id__in={qty['product'] for qty in total_qty_bought}
).first()
# Check if we deal with kanel or peber as a product based on slug
index = 0
if product.slug.startswith("kanel-"):
index = 0
elif product.slug.startswith("peber-"):
index = 1
else:
pass
try:
# Sum the total quantity bought
quantity = 0
for qty in total_qty_bought:
quantity = int(qty["quantity__sum"])
# Get the inventory the order is picked from based on shipment method title
method_title = (
self.shipment_model.get(order_id=total_qty_bought[0]['order_id']) # The error
.method_title.replace("Hent-selv", "")
.strip()
)
# If the order isn't picked, but sent, use this inventory
if method_title not in cities.values():
method_title = "Hovedlager"
try:
# Get the total of kanel and peber bought
kilos = quantity * product_data[product.id]
# If there is no inventory, set it to 0 peber and 0 kanel
if method_title not in our_total:
our_total[method_title] = [0, 0]
# Combine place and kilos
our_total[method_title][index] += kilos
except KeyError as ex:
print(ex)
pass
except WCOrderShipment.DoesNotExist as ed:
print(ed)
pass
# Quantities BOUGHT! (in orders!)
print(our_total)
context = {
"cinnamon_form": self.cinnamon_form,
"peber_form": self.peber_form,
"objects": objects,
"deleted_objects": deleted_objects,
"total_cinnamon": total_cinnamon,
"total_peber": total_peber,
"our_total": our_total,
}
return render(request, self.template_name, context)
You can only do one query by using the __in operator:
shipments = self.shipment_model.get(order_id__in=list_containing_order_ids)
Then you can do a normal for loop in which you verify that condition.
Here I have some field like idnumber in my Profile model.Which will be the mix of integer, text and -. Everytime new profile registers I want to keep the - and text same but want to increase integer by 1. How can I achieve that ?
user = get_object_or_404(get_user_model(), pk=pk)
if request.method == 'POST':
user_type = request.POST.get('type')
user.profile.is_approved=True
user.profile.save()
if user_type == 'Some Type': # i got stuck here
latest_profile = Profile.objects.filter(user_type='Some Type').last()
user.profile.idnumber = latest_profile.idnumber + 1 #this will probably raise the error
user.profile.save()
My model field for id number is like this
idnumber = models.Charfield(max_length=255,default="ABTX-123")
ABTX- will be same but want to increase 123 to 124 if new profile registers
You have to set idnumber as below...
idnumber = int(latest_profile.idnumber.split('-')[1]) + 1
new_idnumber = latest_profile.idnumber.split('-')[0] + '-' + str(idnumber)
user.profile.idnumber = new_idnumber
Is it possible to modify prefetched objects without using save()?
class First(models.Model):
field = models.IntegerField()
class Second(models.Model):
first = models.ForeignKey(First, related_name='seconds')
some = models.IntegerField()
qs = First.objects.filter(field=12).prefetch_related('seconds')
qs[0].seconds.all()[0].some = 999
qs[0].seconds.all()[0] == 999 # should be True
UPDATE:
I found what was wrong. First need evaluate queryset e.g. len(qs) after filter.
I want to alter my question slightly. Can i modify foreign key relationship without hitting db?
qs = First.objects.filter(field=12).prefetch_related('seconds')
len(qs)
print(len(qs[0].seconds.all())) # output: 5
qs[0].seconds.all()[0].first_id = 2
print(len(qs[0].seconds.all())) # output: 4
Instead of doing
f.seconds.all()[0].some = 999
f.seconds.all()[0] == 999 # should be True
Do this..
first_object = f.seconds.all()[0]
first_object.some = 999
first_object.some == 999 #true