Django - Filter based on model method - django

I have a model, defined like this :
class Item(models.Model):
name = models.CharField(max_length=200, null=True, default='', blank=True)
creation_date = models.DateTimeField('date created', default=timezone.now())
def was_published_today(self):
today = timezone.now() - datetime.timedelta(days=1)
return self.creation_date >= today
I would like to filter a set of Item like this :
items = Item.objects.filter(was_published_today=True)
But I run into a FieldError which makes sense as it seems that filter is not made to filter a set of objects based on a method. Detailed error :
Cannot resolve keyword 'was_published_today' into field. Choices are : creation_date, name
What is the correct way to do this ? I know I can compare creation_date with today's date but that's what was_published_today does and I would like to keep it DRY.

Here you can use django Managers.
Custom Manager:
class GetPublishedToday(models.Manager):
def get_query_set(self):
return super(GetPublishedToday, self).get_query_set().filter(creation_date= timezone.now() - datetime.timedelta(days=1))
in Models:
class Item(models.Model):
name = models.CharField(max_length=200, null=True, default='', blank=True)
creation_date = models.DateTimeField('date created', default=timezone.now())
was_published_today= GetPublishedToday()
in views:
items = Item.was_published_today.all() # or .filter(name= "foobar")
'''PS: Its an untested code.'''

F expressions is a correct way, I think. Example from documentation:
from datetime import timedelta
Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

Related

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

Django inline model formset with 2 models

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

Django Nested Form - Always Showing Object instead of model details

I'm working on a Django project generated via Mezzanine. I've been able to create my models, however I want to have a form where an admin can select from a list to assign a value in a many to many or a one to many relationship. For example, I have a model for Schemas:
class Schema(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of all Schemas in a given database"""
name = models.CharField(max_length=128, null=False)
status = models.BooleanField(max_length=128, null=False, default=True, verbose_name="Is Active")
description = models.CharField(max_length=65535, null=True, blank=True, default=None)
database = models.ForeignKey(Database, on_delete=models.CASCADE)
pull_requests = models.ManyToManyField(Link)
questions = models.ManyToManyField(Question, blank=True)
comments = models.ManyToManyField(Comment, blank=True)
technical_owners = models.ManyToManyField(Employee, related_name='technical_owners_schemas', blank=True)
business_owners = models.ManyToManyField(Employee, related_name='business_owners_schemas', blank=True)
watchers = models.ManyToManyField(Employee, related_name='watchers_schemas', blank=True)
def __unicode__(self):
return "{}".format(self.name)
And I have a model for Employees
class Employee(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of people with any involvement in tables or fields: business or technical owners, developers, etc"""
name = models.CharField(max_length=256, blank=False, null=False, default=None, unique=True)
email = models.EmailField(blank=True, null=True, unique=True)
def __unicode__(self):
return "{}".format(self.employee)
An employee can own multiple schemas and a schema can be owned by multiple employees. My database has an active employee in it, however when I try to create a Schema the employee shows up as Employee Object. Rather I would want the form to show the Employee.name. How can I do this? My admin file contains the following:
class SchemasAdmin(admin.ModelAdmin):
list_display = ['name', 'status', 'database', 'description']
ordering = ['status', 'database', 'name']
actions = []
exclude = ('created_at', 'updated_at', 'deleted_at')
First of all are you using python 2 or 3? For 3, the __str__ method should be used instead of __unicode__. I am writing this because it seems that there's a problem with the __unicode__ method of Employee, which although is defined as:
def __unicode__(self):
return "{}".format(self.employee)
th Employee class does not have an employee attribute (unless there's such an attribute in the mixins that class inherits from (AutoCreatedUpdatedMixin, SoftDeleteMixin) but I don't think that is the case.
In any case, the problem is that you haven't defined a propery __str__ (if using python 3) or __unicode__ (for python 2) method on the Employee class - just define it like:
return self.name
and you should see the employee's name in the django admin select fields.

django - use method on every object in queryset like filter '__in' custom manager

Hello I would like to implement method for player to take list of players (query set) and leave clan
I'm looking for something like:
Player.leave_clan([1,2,3])
Player.leave_clan([p1,p2,p3])
What I have tried is the method which takes list of account_ids [1,2,3] and then I use updated method on query, but here I have to pass only account IDs
def remove_leavers(self, leavers, clan):
players = Player.objects.filter(account_id__in=leavers)
players.update(clan=None,
previous_clan=clan)
and with my current models I could call something like:
leavers = Player.objects.filter(account_id__in=[1,2,3]
for player in leavers:
player.leave_clan()
But I do not think it's right way to do. I use following models down bellow, thank you for all suggestion and recommendations.
I think I need custom manager but I did not know how to write it even after reading the documentation.
from django.db import models
class Clan(models.Model):
clan_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
tag = models.CharField(max_length=5)
def __str__(self):
return "{tag}".format(tag=self.tag)
#property
def members(self):
return Player.objects.filter(clan=self)
def kick_player(self, player):
player.leave_clan()
class Player(models.Model):
account_id = models.IntegerField(primary_key=True)
account_name = models.CharField(max_length=250)
clan = models.ForeignKey('Clan',
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='current_clan')
previous_clan = models.ForeignKey('Clan',
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='previous_clan')
def __str__(self):
return '{0} - {1}'.format(self.account_name, self.account_id)
def leave_clan(self):
self.previous_clan = self.clan
self.clan = None
self.save()
If you want to use a Manager you can do this :
class PlayerManager(models.Manager):
def leave_clan(self, players):
"""
Take a list of player. And removed them from their clan
"""
for player in players:
player.leave_clan()
class Player(models.Model):
account_id = models.IntegerField(primary_key=True)
account_name = models.CharField(max_length=250)
clan = models.ForeignKey('Clan',
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='current_clan')
previous_clan = models.ForeignKey('Clan',
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='previous_clan')
def __str__(self):
return '{0} - {1}'.format(self.account_name, self.account_id)
def leave_clan(self):
self.previous_clan = self.clan
self.clan = None
self.save()
How to use it
Player.objects.leave_clan(LIST_PLAYERS)
Hope it helps you.

Django instance - Combine two attribute values in model

Can someone tell me how to combine two or more attribute values of in another field by using instance?
Models.py:
fn_id = models.ForeignKey(FilemNumber, null=True, blank=True)
ln_id = models.ForeignKey(LineNumber, null=True, blank=True)
pn_id = models.ForeignKey(PhotoNumber, null=True, blank=True)
title = models.CharField(max_length=255,blank=True, null=True)
I want to combine fn_id, ln_id and pn_id and save the combination of the three values into field title.
You can do this:
from django import models
class BaseModel(models.Model):
fn_id = models.ForeignKey(FilemNumber, null=True, blank=True)
ln_id = models.ForeignKey(LineNumber, null=True, blank=True)
pn_id = models.ForeignKey(PhotoNumber, null=True, blank=True)
class YourModel(models.Model):
common = models.OneToOneField(BaseModel)
# I suppose that you want to get title, so let define title method
# if obj is an instance of YourModel, you can access title like this:
# obj.title
#property
def title(self):
return '{}{}{}{}'.format(self.id, self.common.fn_id,
self.common.ln_id, self.common.pn_id)
Lets read this article: https://docs.djangoproject.com/en/1.8/ref/models/fields/#onetoonefield