Django 1.7, Python 3.0, Postgresql
Thanks for taking time to read this, I expect I am missing something obvious here.
For simplicity, let's say that my models are:
Student
AcademicClass
I am wanting to use Admin Actions to:
1st: Select Students
2nd: Create new yet-to-have-details-filled-in AcademicClass with the previously attached Students attached
Adding actions = [make_new_academic_class] and linking to that page has been fairly straight-forward, but I am completely confused as to how to attach the queryset on to that new AcademicClass.
students = ManyToManyField('mgmt.Student', related_name='classes')
I believe I have everything correct, except this part:
def make_new_academic_class(modeladmin, request, queryset):
for stdnt in queryset:
print(stdnt.id) #obviously want to save this somehow
#then want to insert it in the AcademicClass-Student relationship
return redirect("/school/class/add")
UPDATE
Was told that the best way to do this would be to "pre-populate the form" using the Django API. Working on that.
Ok, so I figured this out:
def make_class(modeladmin, request, queryset):
new_class = Class()
new_class.save()
for student in queryset:
new_class.students.add(student.id)
return redirect(reverse('admin:mgmt_class_change', args=(new_class.id,)))
I took a couple approaches on this, and this is the one that worked. Hopefully helpful to someone (or myself) in the future.
Related
I have a "big" db whith over 60M records, and I'm trying to paginate by 50.
I have another db whith ~8M records and it works perfectly, but with the 60M amount it just never loads and overflows the db.
I found that the problem was the order_by(id) made by django so I tried using a mysql view already ordered by id, but then django tries to order it again. To avoid this, I used order_by(), which is supposed to avoid any ordering, but it still does it.
def get_queryset(self, request):
qs = super(CropAdmin, self).get_queryset(request)
qs1 = qs.only('id', 'grain__id', 'scan__id', 'scan__acquisition__id',
'validated', 'area', 'crop_date', 'matched_label', 'grain__grain_number', 'filename').order_by()
if request.user.is_superuser:
return qs1
The query made is still using order_by:
SELECT `crops_ordered`.`crop_id`,
`crops_ordered`.`crop_date`,
`crops_ordered`.`area`,
`crops_ordered`.`matched_label`,
`crops_ordered`.`validated`,
`crops_ordered`.`scan_id`,
`crops_ordered`.`grain_id`,
`crops_ordered`.`filename`,
`scans`.`scan_id`,
`scans`.`acquisition_id`,
`acquisitions`.`acquisition_id`,
`grains`.`grain_id`,
`grains`.`grain_number`
FROM `crops_ordered`
INNER JOIN `scans`
ON (`crops_ordered`.`scan_id` = `scans`.`scan_id`)
INNER JOIN `acquisitions`
ON (`scans`.`acquisition_id` = `acquisitions`.`acquisition_id`)
INNER JOIN `grains`
ON (`crops_ordered`.`grain_id` = `grains`.`grain_id`)
**ORDER BY `crops_ordered`.`crop_id` DESC**
LIMIT 50
Any idea on how to fix this? Or a better way to work with a db of this size?
I don't believe order_by() will work as there will most likely be a default parameter when Django implemented this function. Having said that, I believe this thread has the answer that you want.
Edit
The link in that thread might provide too much information at once, although there aren't many details on this either. If you don't like Github, there's also an official documentation page on this method but you'll have to manually look for clear_ordering by using CTRL + f or any equivalence.
I want to order the result of my mongoengine call on two different fields.
Open. This has status True or False. I want this because I want to
display the open questions first
Opendate. Because I want to newest questions to show on top.
Combined this should create a list where I can see on top the open questions (ordered by creation date) and than the already closed questions, also ordered by creationdate.
The code I started with is:
To call the API:
questions = Questions.questions_of_user
To handle the call:
#queryset_manager
def questions_of_user(doc_cls, queryset):
return queryset.filter(questioner=current_user.id).order_by('-openDate')
My first suggestion was to just add 'status' to the order_by (maybe with or without + or - ) would do it. But so far no luck.
Than I tried to only order by the open field, because I thought I was just making an mistake combining the two. So I got this:
#queryset_manager
def questions_of_user(doc_cls, queryset):
return queryset.filter(questioner=current_user.id).order_by('-open')
That however did not work as well. I hope someone can help me out. Thanks in advance
More than one key can be passed in the order_by method of the queryset.
queryset.filter(questioner=current_user.id).order_by('-open', '-openDate')
I'm implementing likes on profiles for my website and I'm not sure which would be the best practice, a ManyToManyField like so:
class MyUser(AbstractBaseUser):
...
likes = models.ManyToManyField('self', symmetrical = False, null = True)
...
or just creating a class Like, like so:
class Like(models.Model):
liker = models.ForeignKey(MyUser, related_name='liker')
liked = models.ForeignKey(MyUser, related_name='liked')
Is one of them a better choice than the other? If so, why?
thanks
The first option should be preffered. If you need some additional fields to describe the likes, you can still use through="Likes" in your ManyToManyField and define the model Likes.
Manipulating the data entries would be also somewhat more pythonic:
# returns an object collection
likes_for_me = MyUser.objects.filter(pk=1).likes
instead of:
me = MyUser.objects.filter(pk=1)
likes_for_me = Like.objects.filter(liked=me)
The second option is basically what is done internally: a new table is created, which is used to create the links between the entities.
For the first option, you let django do the job for you.
The choice is certainly more about how you want to do the requests. On the second options, you would have to query the Like models that match you model, while on the first one, you only have to request the MyUser, from which you can access the connections.
Second option is more flexible and extensible. For example, you'll probably want to track when like was created (just add Like.date_created field). Also you'll probably want to send notification to content author when content was liked. But at first like only (add Like.cancelled boolead field and wrap it with some logic...).
So I'll go with separate model.
I think the one you choose totally depends on the one you find easier to implement or better. I tend to always use the first approach, as it is more straightforward and logical, at least to me. I also disagree with Igor on that it's not flexible and extensible, you can also initiate notifications when it happens. If you are going to use the Django rest framework, then I totally suggest using the first method, as the second could be a pain.
class Post(models.Model):
like = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='post_like')
Then in your view, you just do this.
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def like(request, id):
signed_in = request.user
post = Post.objects.get(id=id)
if signed_in and post:
post.like.add(signed_in)
# For unlike, remove instead of add
return Response("Successful")
else:
return Response("Unsuccessful", status.HTTP_404_NOT_FOUND)
Then you can use the response however you like on the front end.
I have a model with a huge amount of data, and Django creates delete confirmation page a very long time. I have to skip this process and delete data without any confirmation. I have tried some solutions from the internet, but it doesn't work - I still see confirmation page.
Anyone know how to do that?
Django 2.1 released the new ModelAdmin method get_deleted_objects that allows to specify parameters to the confirmation screen for both single and multiple delete (e.g. using the delete action).
In my case, I wanted to delete a list of objects with several relationships, but set with cascade deletion. I ended up with something like this:
def get_deleted_objects(self, objs, request):
deleted_objects = [str(obj) for obj in objs]
model_count = {MyModel._meta.verbose_name_plural: len(deleted_objects)}
perms_needed = []
protected = []
return (deleted_objects, model_count, perms_needed, protected)
I could include other models in the model_count dict, getting only the count, for example, to still avoid list thousands of minor instances that I don't need to see individually.
def delete_selected(modeladmin, request, queryset):
queryset.delete()
class SomeAdmin(admin.ModelAdmin):
actions = (delete_selected,)
I've got two models like this (simplified):
class Post(models.Model):
content = models.TextField('content')
#...
class Vote(models.Model):
user = ForeignKey(User)
status = models.TextField(choices = some_choices, default = 'likes')
post = models.ForeignKey(Post)
#...
What i want to do is to select (using one query) posts using some filter with one particular (let's say current) user's votes on this posts (it's okay if he didn't vote for it), so I can then output all posts and the user can see which ones he liked, which ones he didn't and which ones he didn't vote at all.
select_related for Vote model will not help here, because related objects cannot be filtered, so I guess I should do something with extra, but I cannot figure out what arguments should I pass.
So I guess, it should be something like:
Post.objects.filter(content__contains="test").extra(tables="app_vote", where={'my_vote_status': 'something here perhaps?'})
Could you please help me to understand how make a query like this?
UPD: schacki provided a good solution, the only problem is that I want to access votes by different users from the template, somtehing like Post.vote_by_me and Post.vote_by_this_user or Post.vote_by_top_user
Well, If you proper want answers, sometimes you should look for them yourself :)
Here's how I've solved my problem:
posts = Post.objects.filter(content__contains="test"
).extra(select={
#extra selects vote status here for current user
"my_vote_status":"""SELECT status FROM blog_vote as vt
WHERE vt.user_id = %s
AND vt.post_id = blog_posts.id
""" % (request.user.pk) #
}, tables=['blog_vote'])
UPD: probably would work without tables argument
If I understand your requirements correctly, you will need two objects to pass into context. Try like this, where me and other_user must be valid user objects.
posts.vote_by_me=Post.objects.filter(content__contains="test",vote_set__status="something here perhaps?",vote_set__user=me)
posts.vote_by_other_user=Post.objects.filter(content__contains="test",vote_set__status="something here perhaps?",vote_set__user=other_user)
It's very difficult to understand what you want, but here is another attempt. First, get your posts:
posts = Post.objects.filter(whatever)
Now you want all the votes by a group of users on the posts, correct?
votes = Vote.objects.filter(post__in=posts, user__in=users)
Now all you have to do is associate the votes to the posts based on, say, the user id:
votes_by_user_by_post = defaultdict(lambda: defaultdict(list))
for v in votes:
votes_by_user_by_post[post.id][v.user_id].append(v)
for post in posts:
post.votes_by_user = votes_by_user_by_post[post.id]
Performance-wise, it's fine to do this in two queries plus some scripting. They aren't complicated queries and the scripting part is just two for-loops.