-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Request] Document customizing the class based views #55
Comments
@morenoh149 you can always inherit from the model and override the functionality to be the one you prefer or optionally you can always use a proxy model. Something like this: class MyCustomModel(Message):
class Meta:
proxy = True
def new_message(...):
# overrides here |
This seems to be a problem with the whole app, that you can easily customize the templates, but not the logic in the views.... :/ |
@reedjones that is odd, have you tried to also inherit from the views? Is the same as overriding the models but using the |
@tiagoarasilva yeah that might be my problem that I'm not really familiar with class based views. maybe in the documentation could add an example of how to customize the views? There is a reference on customizing the templates (which is easy) but there is no info on how to customize the logic in the views. For example I need to paginate the messages in the inbox views, I need to check if a user is blocked before sending a message, I need to check the content of a message if it has some banned content (like external links for example) before sending the message, etc.... Does anybody have some concrete examples of how to add custom logic to the views? |
@reedjones I think I can provide you a few examples of how to work with class-based views but bare in mind that I don't work for pinax :-). I'm just a software (python more specifically) engineer that is trying to help a fellow developer 😄 Ok, let's go by phases. Based on what you just said, I assume you work a lot with function-based views, like this: def home(request):
if request.method == 'GET':
# do something
elif request.methods == 'POST':
# another something Is my assumption correct? Let's transform this into a class-based view from django.views.generic import TemplateView # You also have FormView, CreateView, UpdateView, ListView ....
class HomeView(TemplateView):
template_name= 'home.html' # location of yout HTML file in the same way as the render_template in the functions
def get(self, request, *args, **kwargs): # Also a default inherited from every template view of django
if not request.user.is_authenticated:
# DO SOMETHING LIKE RETURNING A REDIRECT OR RAISE A 404
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
# DO THE POST LOGIC HERE
return super().post(request, *args, **kwargs) This is a small example and is only used for explaining how we can achieve both function and class-based views 😄 Lets override using pinax-message specific views (example purposes)Ok, using the example of the Django templates from pinax-messages (the Django Rest Framework is the same thing, actually, I've implemented DRF on top of pinax-messages, which means that I'm not even using the template system provided by them) and what you want to achieve: "For example I need to paginate the messages in the inbox views, I need to check if a user is blocked before sending a message, I need to check the content of a message if it has some banned content (like external links for example) before sending the message, etc.... Does anybody have some concrete examples of how to add custom logic to the views?" Let's try to demystify this and using an example where you can adapt and do for you own needs. from django.views.generic import ListView # This is where the pagination magic will happen
from pinax.messages.views import InboxView
from django.core.paginator import Paginator
from django.contrib.auth.mixins import LoginRequiredMixin
from pinax.messages.models import Thread
class MyInboxListView(LoginRequiredMixin, InboxView, ListView):
"""
We inherit from the mixin LoginRequiredMixin that does the same as the decorator login_required.
Also, we would like to use the InboxView just for the sake of this example and inherit the context_data already there.
In the end we apply that to a ListView to paginate
"""
template_name = 'inbox.html' # location of your inbox html page
paginate_by = 10 # OR WHATEVER NUMBER YOU DESIRE PER PAGE
pagination_class = Paginator # OR NumberDetailPagination
def get_queryset(self):
# Django ListView by default needs you to implement a queryset in order to apply pagination properly
# We are assuming you are using pinax-messages models, if not, you just need to apply to your models.
return Thread.inbox(self.request.user) This, in theory, return all the inbox messages for the logged in user and that def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.kwargs.get("deleted", None):
threads = Thread.ordered(Thread.deleted(self.request.user))
folder = "deleted"
else:
threads = Thread.ordered(Thread.inbox(self.request.user))
folder = "inbox"
context.update({
"folder": folder,
"threads": threads,
"threads_unread": Thread.ordered(Thread.unread(self.request.user))
})
return context Automatically and the reason for it is because we are inheriting the context data but we can add more details to our view. How? Inside our def get_context_data(self, *args, **kwargs):
"""
We will be calling context = super().get_context_data(*args, **kwargs) because we want to get the inherited context data from
inherited views + our own logic
"""
context = super().get_context_data(*args, **kwargs) # here is where your confusion is.
context.update({
'my_new_details_stuff': 'Value added on my MyInboxListView level view, only!'
})
return context Now, we want to send messages and run validations to understand if the user is blocked or not and all of that, so in your views: from django.views.generic import ListView # This is where the pagination magic will happen
from django.views.generic import TemplateView
from pinax.messages.views import InboxView, MessageCreateView
from django.core.paginator import Paginator
from django.contrib.auth.mixins import LoginRequiredMixin
from pinax.messages.models import Thread
from django.contrib import messages
from django.http import PermissionDenied, HttpResponseRedirect
class MyInboxListView(LoginRequiredMixin, InboxView, ListView):
...
...
class SendMessageView(LoginRequired, MessageCreateView, TemplateView):
"""
Since we are inheriting from the MessageCreateView, we don't need to worry about that logic,
We can just implement ours. We can override everything, GET, POST by applying specific logic on each
HTTP verb or in general on the dispatch level
"""
def dispatch(self, request, *args, **kwargs):
if request.user.is_blocked: # OR WHATEVER LOGIC YOU HAVE IN YOUR USER
message.add_message(request, messages.ERROR, "You can't send messages")
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
# SEND MESSAGE LOGIC HERE
messages.add_message(request, messages.SUCCESS, "Message Sent")
return HttpResponseRedirect('your reverse url') This was very generic and I hope it helps you understand how to override and/or apply your own logic and still use pinax-messages resources. Personally, if I was pinax, I would put this also for django rest framework and it would be easier to work async but that is just me. I built my own. Also, bare in mind that you can also use |
@tiagoarasilva Thanks for the detailed reply! :) So I guess by defining my own views with the same names, the url dispatcher will send to mine first? |
@reedjones by default it will always go to your view first since the URL that you defined is pointing to your view and not the pinax (not declared in your app by importing them). Then, your view inheriting from the rest, the validations go from bottom-up (like any normal inheritance class, function, or paradigm), so you absolutely got it. "That's what wasn't clear to me, and honestly I still don't see how will django know to use my view, instead of the pinax" Simple, you have 2 options.
Makes sense? |
@tiagoarasilva |
@reedjones @tiagoarasilva Thank you for the assistance, Tiago. Even some of us who work with Pinax are overwhelmed at how to do things and explain at times. Now that a release is done, docs are a focus. Perhaps this info could be used, if you are ok with that. |
@KatherineMichel please by all means use what you need and want as long I could help. I would do it also for Django rest framework but that is a different example |
@KatherineMichel if you ever consider applying pinax-messages for DRF, please let me know since I have that already implemented as well 😄 |
if welcome, we should add the explanation at #55 (comment) to the documentation in the readme. OR better, add an example django app to the repo OR add a docs page (read the docs or github wiki). @KatherineMichel what is your preference? |
@morenoh149 I've tried to contribute to pinax here #58. I can start doing it and also provide some DRF integrations if you all wish as well since DRF is widely used or just simply create a new pinax-messages-drf |
@tiagoarasilva @morenoh149 I would love to have a DRF example. We have had a lot of DRF interest recently. I think it would probably be best to put it in its own repo. We can link to it in the README and put a reference to it in the global docs. I will review your PR soon @tiagoarasilva. Thank you for submitting! I'm still thinking about what to do with the docs. If you ever have time @tiagoarasilva, would also love to pick your brain if you have any thoughts about how to approach Pinax onboarding for newcomers. I have a tutorial in process, but am sort of at an impasse about how to proceed. |
@KatherineMichel that is not a problem at all. I can just create a pinax-messages-drf and go from there, then you just need to fork it I would say? |
@KatherineMichel I've created a quick but working and tested version using drf here https://github.com/tiagoarasilva/django-messages-drf if you want to have a look and if you are happy, we can have a chat to understand the needs. I didn't want to call pinax for obvious reasons but is heavily based on it of course :) |
imo, a drf example within this repo would be best. In my experience having examples outside of the project makes it more likely to go stale. |
So @morenoh149 what would you like to do? I just tried to help anyway. I've implemented a DEF version on my local a few months ago. Also, by quick I mean, took time but it wasn't a full scalable platform, only the drf version for pinax. I'm happy todo whatever for instance, I'm happy to donate it to pinax |
@KatherineMichel needs to weigh in. Whether the example should be in a separate repo or inside this repo as another example. |
I understand what you are saying, @morenoh149. We do have some projects that have gone stale, but a lot of the most popular ones are still maintained. The reason why I kind of want to keep it separate (for the time being, at least), is that one of the problems we've had in the past is that devs have done different things in different repos (there are dozens of Pinax apps), which I've been working to fix to make them easier to document. I don't think we have a project included in any other app repo. Once something like that is done in one repo, it makes the approach inconsistent across apps. |
So, the conclusion is to ignore and pinax will make a DRF example? |
@tiagoarasilva No, I think your version would be great to have. I think it would be better for it to be in its own repo right now, which is the original plan. |
Ok @KatherineMichel perfect then. I didn't add a lot of documentation but I mentioned that is based on pinax-messages so you can do as you wish. I hope I could help. I've added also some personal snippets that I've been doing for years regarding pagination, metaclasses and so on |
@tiagoarasilva tiagoarasilva Thank you for your explanation. It helped me a lot. Thanks |
@svb0866 I will try to provide some more examples if @KatherineMichel agrees as that takes me some more time and I'm currently on holiday but I will do my best. Is actually quite simple and I will try to do it as soon as possible but summarising, is pretty much overriding current definitions from the parents with the same names on the children classes where the children classes with the same attribute names can have different properties and definitions and when running the migrations, it will use yours instead of the current parent ones. Also, changing existing model fields on the current pinax models it would be possible if you change the source code or having your own version but that would make it not maintained by pinax but if you override in your inherited models the attributes you would have the same effect. I'm not sure if I'm being clear. |
Thanks, @tiagoarasilva ! Enjoy your holiday. |
@svb0866 but thank you for your kind words, I just try to help as much as I can :-). About what I said, even without examples at the moment, I hope it could give some clarity |
Hello, I am getting error: name 'dispatch' is not defined. Specifically I am trying to Inherit from MessageCreateView to run logic before and after a message is sent. The code is giving me dispatch is not defined when trying to return dispatch(request, *args, **kwargs).
|
@devinhadley I could spot your return. You should do |
Thank you so much for your response! I actually did try this earlier and it did not work. But to be safe I copied your code and pasted it in and still got the error: 'SendMessageView' object has no attribute 'object'. Edit: I fixed this placing this within the dispatch method: Although now I am getting an error: SendMessageView is missing a QuerySet This is peculiar as I wish to display a form with no intention of Querying for any models. |
Hi @devinhadley that is actually odd since I did the same and it works like a charm so it would be great if you could provide some additional information like, python version, version of Django and more code snippets. That was merely informative but a queryset is only required if you are inheriting from a view that implements a ListView. A CreateView shouldn't give you that error at all but an UpdateView would for instance. |
Yeah that what I am saying, it is very odd. Python Version: 3.8.5 Class View (Same problem happens when I inherit from CreateView rather than TemplateView):
URL Routing:
Edit: Issue may reside with self.object = self.get_object(). Although without this I get the error as described above. |
Oh, @devinhadley mine is this class SendMessageView(LoginRequired, MessageCreateView, TemplateView):
"""
Since we are inheriting from the MessageCreateView, we don't need to worry about that logic,
We can just implement ours. We can override everything, GET, POST by applying specific logic on each
HTTP verb or in general on the dispatch level
"""
def dispatch(self, request, *args, **kwargs):
if request.user.is_blocked: # OR WHATEVER LOGIC YOU HAVE IN YOUR USER
message.add_message(request, messages.ERROR, "You can't send messages")
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs) I don't declare any self.object like you are doing but in this case I think I know what are you trying to do. If you want a self.object like that you still need to declare your get_object() inside your class. The Django documentation about the CreateView explains that really well but I can see at least in my example that self.object is not declared by any function or overwritten anywhere and I reckon that is where your code is failing since there is no self.object or no declaration of a get_object() anywhere declared in your view |
Lol, this is very odd, I deleted the self.object and now it wants to work just perfectly. Thank you so much for your time and patience! |
@devinhadley I'm glad I could help you :-) |
Oh, golly the post method appears to be skipped, a print statement is not printed to console after submitting the form. Maybe because when you return super().dispatch() it defaults back to the original class view? |
Btw, @morenoh149 @devinhadley @KatherineMichel @svb0866 @reedjones https://github.com/tarsil/django-messages-drf is already in the version 1.0.3 and is what we talked about before but I made a lot of changes from the original design and added a lot of my own for DRF :) |
The create view is described at https://github.com/pinax/pinax-messages#create-message---code
I would like to do some extra logic here. How can I customize the create view?
The text was updated successfully, but these errors were encountered: