Should I pass a dictionary to my template? - django

Being a newb I'm trying to work out what belongs where. I've got a ListView to list all users in the system, and it includes a get_queryset method -something along the lines of:
def get_queryset(self):
users = []
for user in User.objects.all():
a_user = {}
a_user['username'] = user.username
a_user['full_name'] = user.get_full_name()
a_user['num_of_friends'] = len(user.friends.all())
a_user['phone_num'] = user.get_profile().phone_num
a_user['has_subscription'] = bool(Subscription.objects.filter(subscriber=self.request.user))
users.append(a_user)
return users
So rather than returning a queryset of users I'm making this dictionary out of various chosen attributes of each user, and what a template designer gets is limited to only what I think they should have.
Is it better practice to instead pass user objects to the template, and let the template writer get whatever they need from them? I suppose the answer is Yes.
But, when it comes to the a_user['has_subscription'] line above, I assume the answer becomes No?

The way you've done it is totally fine. I've found it to be useful to separate my templates from my model by explicitly providing the information needed by the template in the view, which is essentially what you've done. It's a little bit weird to not be returning an actual queryset from a method called get_queryset, but I've also come to the conclusion based on criticisms like this that class-based views as they are currently implemented in Django should be considered a means to an end, as opposed to a dogmatic way of organizing your view code. The abstraction simply isn't as clean as say, the Django ORM.
If you really wanted something a little slicker, you could try using the values method to narrow your queryset, with an annotation to add in the subscription count. But there's probably not much point.

It depends on what you're trying to do with the array 'users'. Someone editing the template can only style and display the data in a certain way. The get_queryset function can provide the data for the template.

Related

How can I redact a Django object instance based on the user's view permission?

I would like to redact object instances based on the user's view permission.
For instance, if I have the following model
class Data(models.Model):
value = models.FloatField()
I would like users that have view permission, i.e. myapp.view_data, to see the full value, and users without view permission to only see a redacted version, say round(value).
I could implement this for every view and template, but that doesn't seem very DRY and would have a risk of overlooking some occurrences. Also, I suppose in a template the mechanism might be bypassed through relation queries.
How can I implement this redaction mechanism in the model? Would this be good practice? Or is there a better way?
Thank you!
You can make a class method called get_modified_value(user). This would basically be a wrapper for the value field. It would take a User object as a parameter. Based on that user's permissions, it would either return the raw value or the rounded value.
I recommend this because Models are completely unaware of context or the http request. So no matter what, every time you want that value, you are going to need to do some sort of manipulation in the view. It seems cleanest to me to pass user to the model method but that might have its own problems.

built in methods for Django CBV's

I am learning Django out of pure passion and I seem to have trouble understanding some core principles of Django that really give me headackes and i tried searching online and reading the docs but i don't seem to fully understand. I'll just simply shoot the questions and try to be clear . I apologise for anything stupid I say . I am just trying to get my knowledge straight.
1. What is a "request"?
OK, so I am thinking of a GET request for a webpage but online i see python code like self.request.user.is_superuser and i am thinking of an User object fetched and displayed to the template to which i can apply User methods. It is clearly more to this request than i already know.
2. CBV's built in methods. The get methods. How do they execute? In what order.
I noticed programmers override these methods sometime . If i redefine more than one method in a CBV i start getting weird behaviour.
For example if i declare and redefine 2 methods in a Detail View get_object() and get_queryset() which of them will be executed first after calling the view? Being a method it should be called somehow in order to execute but i don't know the order how these methods are called for a CBV if there is more than one. Probably i should only define one of them, not both.
3. Queryset. Is it a list?
I have an ideea what querysets are and that you can apply filters to them but what do they return? Do they return objects of the model i am querying?.
OK so, if i have a DetailView CBV and override the get_queryset() method will this return on object and pass it to my template?. Here I am using a filter but what if am not using a filter. What will the CBV return? Will it return the entire set of objects?
class UserDetailView(LoginRequiredMixin,DetailView):
context_object_name='user_detail'
model=models.User
template_name='basicapp/user_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs
4. Can you kindly recommend a Udemy course, video course or book where querysets, CBV methods and ORM are explained clearly?
Preferably video cause it makes so much difference when i see the code working
The thing is I understood how the MVT mechanism works but these class built in methods and overriding them gives me a lot of problems . I never encountered them before and when i start using them I feel like as if i am walking in the dark .
Sorry for the long post.
Thanks
What is a request?
It is a HttpRequest object [Django-doc]. This is an object that contains data about the request the client made to the server. For example this contains an attribute .method [Django-doc] that contains a string like 'GET', 'POST', 'PATCH', etc. that specifies the request method used.
but online i see python code like self.request.user.is_superuser and I am thinking of an User object fetched and displayed to the template to which I can apply User methods.
One can install middleware [Django-doc], this is tooling that pre-processes the request, or post-processes the response. Normally the AuthenticationMiddleware [Django-doc] is added in the settings.py file by default. This will add an extra attribute .user to the request that lazily loads the user that has been logged in. If you would remove this middleware, the .user attribute will no longer exist.
CBV's built in methods. The get methods. How do they execute? In what order.
That depends on the specific class-based view. The documentation however specifies how most methods are performed. For example for a ListView [Django-doc], the documentation mentions the method flowchart:
Method Flowchart
setup()
dispatch()
http_method_not_allowed()
get_template_names()
get_queryset()
get_context_object_name()
get_context_data()
get()
render_to_response()
It also links to the methods that explain what these are doing.
In essence, each class-based view can have methods like get, post, put, patch, etc. Based on the method of the request, the request is dispatched to the method with the same name.
Queryset. Is it a list?
No. A QuerySet is an object that more or less represents a query that you can perform on the database. QuerySets are lazy. That means that as long as you do not iterate over them or for example call len(…) over these, they will not make that query. If you iterate, etc. over these, they will make a query to the database, and then you iterate over the result of the query. The results are normally wrapped in model objects (unless you use functions like values(…) [Django-doc] or values_list(…) [Django-doc].
Can you kindly recommend a Udemy course, video course or book where querysets, CBV methods and ORM are explained clearly?
It might help to start with the Django tutorials. These go step-by-step over the architecture of Django. The documentation on the QuerySet API explains how you can make querysets. The page on Query expressions gives examples on how to make more advanced queries. Finally the Django documentation also has a page on class-based views.

Return QuerySet from a RawQuerySet

I am trying to create a queryset from a rawqueryset, but the changelist is always saying Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user.
This is my attempt (is a simple query, but it will became more complex and I need a raw SQL query):
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = MyModel.objects.raw('SELECT field1, field2 FROM MyTable)
return qs
Is there any way to show this raw query in my changelist view applying admin.site.register(MyModel, MyModelAdmin) in admin.py?
It's been a while, and versions seem to have changed a lot since then, however you used to be able to simple convert the qs itself to a string or the query attribute contained within the qs, and it would spell out the sql for you.
e.g. sql = str(qs) or sql = str(qs.query)
That being said, django modifies the table names. It's probably not MyTable you're looking for, but appname_my_table. Browse the django core to find out exactly what the naming specification is, or perhaps get it from model_instance._meta.db_name or similar property.
update: ok it seems I misunderstood the question, you don't want to 'print' the queryset to inspect the sql it generates at all, you need the ModelAdmin to pass a RawQuerySet instead of a regular QuerySet to its ChangeView.
Short answer: No, its not going to work that way. They are two very distinct classes.
It looks like they behave the same, because you can iterate them and when you do it turns out to contain proper model instances. However its missing a plethora of other functionality that the ModelAdmin's changeview has come to rely upon over time. I'm not even sure if it actually has a Manager.
The only option that comes to mind, is using either:
Use Model.objects.extra(...) instead of Model.objects.raw(...), so that it returns a proper QuerySet.
Create a view using your database software, Postgres or MySQL, what have you. And map that to a simple django Model.
You can try and recreate all the missing functionality by wrapping the RawQuerySet in a little proxy class that passes everything on to the RawQuerySet and implement the missing methods yourself. I do not recall which those were, probably you need to override __and__ and __or__, provide it with a custom manager, at the very least. I've done this before and its very possible but I guarantee a lot of work, especially if you expect ALL of the usual admin magic to work, think filterspecs, search, inline relationships. Expect to hurt your brain, or learn to live without most of the admins benefits you've come to love.
The thing is, using raw(...) basicly results in being entirely seperated from django's ORM, even though at first glance it fools you into thinking that this is not the case (primarily because its iterator returns proper Model instances). Ofcourse your ModelAdmin's ChangeView has not got the slightest clue of what to do with it.
Kind regards,
You can subclass models.Manager and create a method that executes raw SQL as a subquery in the where clause.
https://gist.github.com/carymrobbins/8477219

Specifying default value for django hidden form field - bone DRY?

So let's say at the last minute (in the view) I decide I want to specify a default for a field and make it hidden, like so:
form.fields['coconut'] = forms.ModelChoiceField(
label="",
widget=forms.HiddenInput(),
queryset=swallow.coconuts.all(),
initial=some_particular_coconut,
)
My question is this: Do I really need to specify queryset here? I mean, I already know, from initial, exactly which coconut I'm talking about. Why do I also need to specify that the universe of available coconuts is the set of coconuts which this particular swallow carried (by the husk)?
Is there a way I can refrain from specifying queryset? Simply omitting causes django to raise TypeError.
If indeed it is required, isn't this a bit damp?
I think is good that stackoverflow answers point to the 'right' way to do things, but increasingly the original question goes unanswered because the user was trying to do the wrong thing.
So to answer this question directly this is what you can do:
form.fields['coconut'] = forms.ModelChoiceField(label="", widget=forms.HiddenInput(attrs={'value':some_particular_coconut}), queryset=swallow.coconuts.all())
Notice the named argument passed to HiddenInput, its super hackish but its a direct answer to the original question.
The problem is that you're trying to set up a hidden ModelChoiceField. In order to have a Choice (dropdown, traditionally) it needs to know its Choices - this is why you give a queryset.
But you're not trying to give the user a choice, right? It's a hidden input, and you're setting it from the server (so it gets POSTed back, presumably).
My suggestion is to try to find a way around using the hidden input at all. I find them a bit hacky. But otherwise, why not just specify a text field with some_particular_coconut.id, and hide that? The model's only wrapping that id anyway.
The reason django requires a queryset is because when you render the field to the page, django only sends the id. when it comes back, it needs knowlege of the queryset in order to re-inflate that object.
if you already know the queryset at form creation time, why not simply specify form.fields['coconut'].initial = some_particular_coconut in your view and leave the rest of the definition in your forms.py?
If you find that you only really need to send the id anyway (you don't have to re-inflate to an object at your end), why not send it in a char field?

Working programmatically with an HTTPResponse in Django

I am working on an app which would enable a preview function for a model. Models marked as preview-able would allow for changes to be made in the Django admin interface and previewed on site using the same view as would an object of that type normally use to render itself, but rendered instead with the new (unsaved) object in it's place.
This is a pretty easy task to do in a bespoke fashion when you know the views or templates ahead of time. But I want this to be reusable and simple.
What I Was Thinking
My idea would be to apply the resolve() urls function to the existing (saved) object's get_absolute_url() value to discover the view used dynamically. Then call that view, get the returned HTTPResponse and alter it in some fashion before returning it myself.
The Problem
It seems that by the time the HTTPResponse is returned by the object's natural view the HTML has already been rendered by the template engine.
So I guess the question is: Is there a way to get at a HTTPResponse before the template is rendered and alter the context variables before that happens.
If not then could the same idea be implemented in another fashion. Would using middleware change anything (your still working with a HTTPResponse object there as well).
Thanks,
Marcus
P.S. If you come up with a radically different methodology to solve this problem, I will be sure to attribute that concept to you in the app documentation (despite it being a small app right now).
It is not trivially possible no, the easiest way would actually be to write your own template context processor that checks for example if something like GET['preview'] is set, then sets dictionary values based on some other GET or POST data. Furthermore when other variables are added it should make sure these don't overwrite the existing values set by this method (otherwise the view would override it anyway with some other data).
One remark however: completely unintrusive behaviour will often lead to erroneous behaviour. If the view does not know of this preview functionality and e.g. it expects a valid id or redirects to an error page, your preview won't work (as you don't really have a valid id). Choosing for views that know of this preview functionality is indeed some more work but will certainly be correct. You could try to make it more generic by using decorators or callable view classes (which can be derivable from some generic base) instead of view methods.
A completely different approach that is only slightly 'view' intrusive, I assume you do not want to save the model so it doesn't show up in public listings. An approach could be to add a 'preview' field and use managers to restrict lookups, so something like this:
class NoPreviewManager(models.Manager):
def get_query_set(self):
return super(MyModelManager, self).get_query_set().filter(preview=False)
class MyModel(models.Model):
... #other fields
preview = models.BooleanField()
objects = NoPreviewManager()
allobjects = models.Manager()
In all normal views you can just use MyModel.objects so previews won't be listed. In the object-specific view you use MyModel.allobjects to also enable defailed views of previews. This way you don't have to do weird view hijacking things, but you should take care preview objects get cleaned up if they aren't promoted to real objects. Note that you can also combine many of this logic into a base class.