Django: want to sort comments by datetime - python

I have in my view comments and I want to sort them with the latest comment at the top of the list. However it is not working. I get this error.
Caught TypeError while rendering: 'Comment' object is not iterable
I am not so sure what is causing this problem. Here is my views and model which may help.
Views
def home(request):
comments = Comment.objects.latest('datetime')
return render_to_response('home.html', {'comments':comments}, context_instance=RequestContext(request))
models
class Comment(models.Model):
name = models.CharField(max_length = 40)
datetime = models.DateTimeField(default=datetime.now)
note = models.TextField()
def __unicode__(self):
return unicode(self.name)

The cleanest way is to add a class meta to your model and add the ordering parameter like this:
class Comment(models.Model):
name = models.CharField(max_length = 40)
datetime = models.DateTimeField(default=datetime.now)
note = models.TextField()
class Meta:
ordering = ['-datetime']
def __unicode__(self):
return unicode(self.name)
So every query you make will be ordered by datetime.
Another advice do not choose "datetime" as a field name, datetime is a python module included in the standard lib.
Also see Django ordering docs here.

The latest method returns only one object, not an iterator:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#latest
Use the order_by method to order them by date (first example in the doc):
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.order_by

Comment in comments = Comment.objects.latest('datetime') is NOT a collection of comment; it is a single comment.
What you want to do is create an array of Comment objects and iterate through that.

Related

How to loop through a jsonfield serializer

I've been trying to loop through the incoming data from a json serializer for a couple of weeks now. Tried a few different approaches, but i can't seem to loop through the json and return a list with all facility_id's for example. Ultimately i want to create one leadfacility object for each json item using it's facility_id and it's datetime. But I can't even seem to access the facility_id when using a for loop.
The facilities that are being assigned are already inside the database.
Does anyone know what I'm missing here? How can i loop though "assigned_facilities"? The only thing I am able to return is the entire json data all at once with print(). Or is my json data structured in the wrong way?
class LeadUpdateSerializer(serializers.ModelSerializer):
assigned_facilities = serializers.JSONField(required=False, allow_null=True, write_only=True)
def create(self, validated_data):
assigned_facilities = validated_data.pop("assigned_facilities")
instance = Lead.objects.create(**validated_data)
for item in assigned_facilities:
instance.leadfacility.create(assigned_facilities_id=assigned_facilities.get('facility_id'), datetime=assigned_facilities.get('datetime'))
return instance
json
{
"assigned_facilities": [{
"facility_id": "1",
"datetime": "2018-12-19 09:26:03.478039"
},
{
"facility_id": "1",
"datetime": "2019-12-19 08:26:03.478039"
}
]
}
models.py
class LeadFacilityAssign(models.Model):
assigned_facilities = models.ForeignKey(Facility, on_delete=models.CASCADE, related_name='leadfacility')
lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name='leadfacility')
datetime = models.DateTimeField(null=True, blank=True)
class Facility(models.Model):
name = models.CharField(max_length=150, null=True, blank=False)
def __str__(self):
return self.name
class Lead(models.Model):
first_name = models.CharField(max_length=40, null=True, blank=True)
last_name = models.CharField(max_length=40, null=True, blank=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
I'd suggest a different, arguably better approach. Instead of JSONField use a nested model serializer.
class AssignedFacilitySerializer(serializers.ModelSerializer):
fields = ('assigned_facilities_id', 'datetime')
model = Facility
class LeadUpdateSerializer(serializers.ModelSerializer):
assigned_facilities = AssignedFacilitySerializer(many=True)
def create(self, validated_data):
assigned_facilities = validated_data.pop("assigned_facilities")
instance = Lead.objects.create(**validated_data)
for item in assigned_facilities:
instance.leadfacility.create(**item)
return instance
You haven't posted your Facility model, so there may be some improperly named fields, maybe the related_name too (leadfacility).
you are doing :
for item in assigned_facilities:
instance.leadfacility.create(assigned_facilities_id=assigned_facilities.get('facility_id'), datetime=assigned_facilities.get('datetime'))
but not using item anywhere.
for item in assigned_facilities:
instance.leadfacility.create(assigned_facilities_id=item['facility_id'], datetime=item['datetime'])
I see a few issues in both your models and how you add facilities to a lead in your loop.
Please take a look at https://docs.djangoproject.com/en/4.1/topics/db/models/#extra-fields-on-many-to-many-relationships and edit your models accordingly.
TL;DR
You can then put extra fields on the intermediate model.
This is LeadFacilityAssign in your case.
The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary.
You are missing this part. Make sure to add a ManyToManyField to Leads.
If you keep reading the docs, you will also find that you can use add(), create(), or set() to create relationships between Lead and Facility using the through model.
You mentioned facilities already exist, so create() is the wrong method.
Please use add() or set() according to your use case and pre-fetch those facilities using their id.
When you use pop() it gets and then deletes the data. That is why u are unable to process it in for loop.
Use get() instead of pop(), and ur problem will be solved.
Change this line:
assigned_facilities = validated_data.pop("assigned_facilities")
to:
assigned_facilities = validated_data.get("assigned_facilities")

django reverse lookup foreignkey not working

I want to get a specific Video object and then find all of the Rating objects that are associated with it using ForeignKey reverse lookup as described in the docs.
I have models:
class Video(models.Model):
...
rating_int = models.IntegerField(default=1, choices=CHOICES, name='rating_int')
def __str__(self):
return self.title
class Rating(models.Model):
video = models.ForeignKey('Video', related_name='video', null=True)
and views:
def video_ratings(request):
vid_rate = Video.objects.get(pk=1)
ratings_of_video = vid_rate.rating_set.all()
context = {
'vid_rate': vid_rate, 'ratings_video': ratings_of_video
}
return HttpResponse(template.render(context, request))
When I try to run this I get an error 'Video' object has no attribute 'rating_set'
But when i read the django docs it tells me when you do a reverse lookup you need to use this _set.all() command. I am not sure what is missing here.
You have specified related_name in your foreign key loopkup. so rating_set should not work now.
You can lookup like
ratings_of_video = vid_rate.video.all()
A better naming convention will be to use ratings in your related_name
class Rating(models.Model):
video = models.ForeignKey('Video', related_name='ratings', null=True)
and then query like
ratings_of_video = vid_rate.ratings.all()

Include Queryset Key in String Format

I am trying to run a system command with the value of a field from a queryset but not having much luck. And it is returning 'Jobs' object is not subscriptable'. Please see below for relevant information.
Models.py
class Jobs(models.Model):
user_id = models.CharField(max_length=100)
template = models.CharField(max_length=100)
change_ref = models.CharField(max_length=100)
summary = models.CharField(max_length=100)
category = models.CharField(max_length=100)
Views
def delete_job(request, job_id):
record = Jobs.objects.get(pk=int(job_id))
os.system('mkdir /home/username/' + record['summary'])
return HttpResponseRedirect("/configen")
I am passing the job_id in through the URL which seems to work fine ( I can delete the record no problem). I was under the impression the 'get' would simply get one record, which I could then reference as a dictionary?
I'm sure there is a simple solution, it doesn't seem to work with string formatting either (using %s or .format()) methods.
Thank you in advance
You're correct that get does get one record, but wrong that you can reference it as a dictionary. It's a model instance, so you use the normal dot notation: record.summary.

django - having model class how to get list of all fields, their types and parameters passed?

lets assume I have following model:
class Note(models.Model):
user = models.ForeignKey(User)
pub_date = models.DateTimeField()
title = models.CharField(max_length=200)
body = models.TextField()
def __unicode__(self):
return self.title
I need a function that will work like this:
print inspectModelClass(Note)
>>> {user:('ForeignKey', {null:False, unique:False, blank:False...}), pub_date:('DateTimeField',{null:False, unique:False,...})...}
I don't know how to list only instances of django.model.field, how to get their names, proper types (BooleanField, CharField, etc.) and their properties like null, unique, max_chars etc.
Can you help me with that?
The reason I need this is that having such a function I would be able to dynamically create Index classes for django-haystack.
You can get Model fields properties easily using the class Metadata.
MyModel._meta.fields
return a list of the fields.
Every field in this list has your well known attributes (name, verbose_name etc.)

Is it possible to sort elements in a ToManyField attribute using TastyPie?

I have a REST API using Django Tastypie. Given the following code
The models
class BlogPost(models.Model):
# class body omitted, it has a content and an author
class Comment(models.Model):
blog_post = models.ForeignKey(BlogPost, related_name="comments")
published = models.DateTimeField()
# rest of class omitted
The resources
class CommentResource:
# omitted
class BlogPostResource(ModelResource):
comments = fields.ToManyField("resources.CommentResource",
attribute="comments")
When I ask for a blogpost I get something like:
GET: api/blogpost/4/
{
'content' : "....",
'author' : "....",
'comments' : ['api/comment/4/', 'api/comment/5']
}
However, the comments are not necessarily sorted by any field. I would like to make sure they are sorted by a specific key (published)
Is there any way to achieve this?
I managed to solve the issue by changing the field in BlogPostResource to the following:
class BlogPostResource(ModelResource):
comments = fields.ToManyField("resources.CommentResource",
attribute=lambda bundle: bundle.obj.comments.all().order_by("published"))
You could also try adding an ordering in the actual Comment Model (not in the tastypie Comment ModelResource):
class Comment(models.Model):
blog_post = models.ForeignKey(BlogPost, related_name="comments")
published = models.DateTimeField()
class Meta:
ordering = ['published']

Categories

Resources