Django : get the last item of manytomanyfield - python

With these models :
class Message(models.Model):
text = models.CharField(max_length=200)
date_send = models.DateTimeField(auto_now_add=True,editable=False)
emeteur = models.ForeignKey(User,null=True,related_name="+")
class Meta :
abstract = True
class MessagePerso(Message) :
read_at = models.DateTimeField(("read at"), null=True, blank=True)
class Conversation(models.Model):
title = models.CharField(max_length=90)
creator = models.ForeignKey(User,null=True,related_name="CreatorConversation")
recipient = models.ForeignKey(User,null=True,related_name="RecipientConversation")
created_at = models.DateTimeField(auto_now_add=True,editable=False)
messages = models.ManyToManyField(MessagePerso)
So, for each Conversation I want the text of the last MessagePerso created (ManyToManyField in Conversation) and others informations.
So for now, I have this :
def .. :
u = request.user
conversations = Conversation.objects.filter(Q(creatorr=u)|Q(destinataire=u)).annotate(nbMsg=Count('messages'),date=Max('messages__date_send')).order_by('date','id')
I don't have the the text of last message created for each Conversation, how makes it ?

You can order your data by a date_send field ascending as follow:
class Message(models.Model):
text = models.CharField(max_length=200)
date_send = models.DateTimeField(auto_now_add=True,editable=False)
emeteur = models.ForeignKey(User,null=True,related_name="+")
class Meta :
abstract = True
ordering = ['date_send'] # default ordrer while fetching data
Than for each conversation's messages you fetch the last using .last():
def .. :
u = request.user
conversation_id_list = Conversation.objects.filter(Q(creatorr=u)|Q(destinataire=u)).values_list('id', flat=True)
message_list = list(()
for id in conversation_id_list:
last_message = MessagePerso.objects.filter(conversation_id=id).last()
message_list.append(last_message)
print(last_message.text)
if the number of conversation is kind of big and you want to minimize db queries you can use prefetch_related
def .. :
u = request.user
conversation_list = Conversation.objects.filter(Q(creatorr=u)|Q(destinataire=u)).preftech_related('messages')
messages_dict =dict()
for conversation in conversation_list:
messages_dict.update({
converstaion.id: conversation.messages.all()
})
last_message_list = list()
for conversation_id, message_list in messages_dict.items():
if message_list:
last_message = messages_dict.get(conversation_id)[len(message_list) -1:]
last_message_list.append(last_message)
print(last_message.text)

Related

get() returned more than one Post -- it returned 2

I got problem in django. Im creating online shop website and I add products section where my products listed(html). I add my products from admin site (models.py).
When I want to add to products i give error like this : get() returned more than one Post -- it returned 2!
These are my codes :
views.py
class PostDetail(generic.DetailView):
model = Post
template_name = "shop-single.html"
def get_context_data(self, **kwargs):
models = Post.objects.get()
context = super().get_context_data(**kwargs)
client = Client(api_key = settings.COINBASE_COMMERCE_API_KEY)
domain_url = "https://www.nitroshop.store/"
product = {"name" : f'{models.title}' , 'description': f'{models.subject}' , "local_price" : {'amount' : f'{models.product_price}' , "currency" : "USD"} , "pricing_type" : "fixed_price" , "redirect_url" : domain_url + "NXnUijYpLIPy4xz4isztwkwAqSXOK89q3GEu5DreA3Ilkde2e93em8TUe99oRz64UWWBw9gEiiZrg60GMu3ow" , "cancel_url" : domain_url + "products"}
charge = client.charge.create(**product)
context['charge'] = charge
return context
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
STATUS = (
(0 , "Draft"),
(1 , "Publish")
)
class Post(models.Model):
title = models.CharField(max_length = 200 , unique = True)
slug = models.SlugField(max_length = 200 , unique = True)
author = models.ForeignKey(User , on_delete = models.CASCADE , related_name = "shop_posts")
updated_on = models.DateTimeField(auto_now = True)
subject = models.CharField(max_length = 200 , default = "We offer you pay with Tether or Litecoin")
caption = models.TextField()
product_brand = models.CharField(max_length = 200 , default = "Add parametr")
product_price = models.CharField(max_length = 200 , default = "Add parametr")
opt = models.TextField(default = "Add parametr")
image = models.ImageField(upload_to = "images/" , default = "None")
created_on = models.DateTimeField(auto_now_add = True)
status = models.IntegerField(choices = STATUS , default = 0)
class Meta:
ordering = ["-created_on"]
def __str__(self):
return self.title
I must be use coinbase gateway for payment. I want when user go to coinbase payment the title of product(each product title) set on coinbase title and ...
But I have error like that when i want add more products
Would you please help me ?
models = Post.objects.get()
This method is to get() a single object from model. If you don't apply any parameters, then it tries to get all objects in QuerySet. If there is more than one (or None), then it will throw an error. And this is happening, because I can assume you have two Post objects in your database.
You need to pass parameter like:
models = Post.objects.get(id=some_id)

Django REST: ignoring custom fields which are not part of model

My TimeReport model looks like this:
class TimeReport(models.Model):
minutes_spent = models.PositiveIntegerField()
task = models.ForeignKey(Task, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
reported_for = models.DateField()
note = models.TextField(null = True, blank=True)
status = models.CharField(max_length=50, choices=State.choices, default=State.new)
user = models.ForeignKey(User, on_delete=models.PROTECT)
And my model serializer:
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task = Task.objects.filter(custom_id = validated_data['task_custom_id']).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report
So, the problem is, that I want to take a custom value in a serializer, which is not a part of a model and do some custom logic with it - in this case search for the right 'task' in the database. But when I try to parse the model by using report = TimeReport(**validated_data), it gives me an exception:
TypeError at /api/report/
TimeReport() got an unexpected keyword argument 'task_custom_id'
Im kind of new to Django and python itself, so - what is the best approach?
If you are going to use that field only for creation, you should use write_only option.
task_custom_id = serializers.CharField(write_only=True)
See the docs here https://www.django-rest-framework.org/api-guide/fields/#write_only
You just need to remove task_custom_id from the dictionary
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task_custom_id = validated_data.pop("task_custom_id")
task = Task.objects.filter(custom_id = task_custom_id).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report
task = Task.objects.filter(custom_id = validated_data.pop('task_custom_id')).filter(user = user.id).first()
the **validated_data will return (task_custom_id=value, field1=value1 ...) and task_custom_id it's not a TimeReport field so all u need is to pop it from validated_data before calling the constructor TimeReport

Django ORM like select query in Pony ORM

I'm now to Pony ORM
and I have created my DB models like this:
class RSSSource(db.Entity):
title = Required(str, max_len=100)
link = Required(str, max_len=800)
key = Required(str, max_len=200)
description = Required(str, max_len=800)
is_deleted = Optional(bool, sql_default=False)
rss_content = Set('RSS', reverse='source')
subscriptions = Set("Subscription", reverse='source')
def __str__(self):
return self.title
class RSS(db.Entity):
title = Optional(str, max_len=300)
link = Optional(str, max_len=500)
description = Required(str, max_len=500)
source = Optional("RSSSource", reverse="rss_content")
pub_date = Optional(datetime)
is_deleted = Optional(datetime, sql_default=False)
likes = Set("Like", reverse="rss")
def __str__(self):
return self.title
class User(db.Entity):
first_name = Optional(str, max_len=100)
last_name = Optional(str, max_len=100)
username = Required(str, max_len=200, unique=True)
password = Required(str, max_len=500)
is_deleted = Optional(bool, sql_default=False)
subscriptions = Set("Subscription", reverse="user")
likes = Set("Like", reverse="user")
def __str__(self):
return self.username
class Subscription(db.Entity):
source = Optional("RSSSource", reverse="subscriptions")
user = Optional("User", reverse="subscriptions")
is_deleted = Optional(bool, sql_default=False)
now I want to perform a Django ORM-like select query. something like this:
select user.id
from Subscription where source.key = $key and (is_deleted is null or is_deleted = FALSE)
I couldn't find a way to do this. can anyone help, please?
I finally came up with something like this and it worked.
select user
from Subscription as s
left join RSSSource as rs on rs.id = s.source
where rs.key = $key and (s.is_deleted is null or s.is_deleted = FALSE)

Django model form field to have a user dropdown list based on a condition

In a Django Modelform (Product_definition), i want to have a dropdown(Merchant name) which will show users only if the their designation in User form is "Merchant".
is it possible that I could get the list of users for the dropdown based on this condition .Please note that i don't require it to be a foreign key as connecting the models is not required.
This is the form which contains the Designation :
from django.contrib.auth.models import User
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE)
#extra UserAttribute
MERCHANT = 'MR'
FABRIC = 'FR'
WASHING = 'WS'
PRINT = 'PR'
PLANNER = 'PL'
DESIGNATION_CHOICES =(
(PLANNER,'Planner'),
(MERCHANT,'Merchant'),
(FABRIC,'Fabric'),
(WASHING,'Washing'),
(PRINT,'Printing'),
)
Designation =models.CharField(
max_length = 20,
choices = DESIGNATION_CHOICES,
default= 'PLANNER'
)
def __str__(self):
return self.user.username
and this is the form with Merchant Name where I want the names of all merchants to appear.
class Product_definition(models.Model):
Order_number = models.CharField(max_length=25,unique = True, blank = True, null = True)
style_name = models.CharField(max_length=15, blank = True, null = True)
color = models.CharField(max_length=15, blank = True, null = True)
Order_qty = models.PositiveIntegerField()
SMV = models.FloatField()
MERCHANT = models.ForeignKey(UserProfileInfo,on_delete= models.CASCADE,default='Select')
def __str__(self):
return self.Order_number
I have created a foreign key for now but I don't require it and it doesn't list the names of only the merchant in the drop down.
I think you can do it like this using ModelChoiceField:
class ProductForm(forms.ModelForm): # please use CamelCase when defining Class Names
MERCHANT = forms.ModelChoiceField(queryset=UserProfileInfo.objects.filter(Designation=UserProfileInfo.MARCHENT)) # Please use sname_case when naming attributes
class Meta:
model = Product_definition # Please use CamelCase when defining model class name
fields = '__all__'

Django bulk_create function example

I'm trying to understand bulk_create in Django
This was my original query I'm trying to convert:
for e in q:
msg = Message.objects.create(
recipient_number=e.mobile,
content=batch.content,
sender=e.contact_owner,
billee=batch.user,
sender_name=batch.sender_name
)
Does that mean doing the following (below) will loop and create all the entries first then hit the database? Is this right?
msg = Message.objects.bulk_create({
Message (
recipient_number=e.mobile,
content=batch.content,
sender=e.contact_owner,
billee=batch.user,
sender_name=batch.sender_name
),
})
The second code in the question create a single object, because it pass a set with a Message object.
To create multiple objects, pass multiple Message objects to bulk_create. For example:
objs = [
Message(
recipient_number=e.mobile,
content=batch.content,
sender=e.contact_owner,
billee=batch.user,
sender_name=batch.sender_name
)
for e in q
]
msg = Message.objects.bulk_create(objs)
The Official Example:
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
Now, to Bulk Create
Entry.objects.bulk_create([
Entry(headline='This is a test'),
Entry(headline='This is only a test'),
])
name = request.data.get('name')
period = request.data.get('period')
email = request.data.get('email')
prefix = request.data.get('prefix')
bulk_number = int(request.data.get('bulk_number'))
bulk_list = list()
for _ in range(bulk_number):
code = code_prefix + uuid.uuid4().hex.upper()
bulk_list.append(
DjangoModel(name=name, code=code, period=period, user=email))
bulk_msj = DjangoModel.objects.bulk_create(bulk_list)

Categories