It's an example that's as similar as possible, and it's not exactly the same as the actual code.
But I believe it's easy to understand.
class Fruits:
...
def get_sample_data(self, df):
...
data = {
'put_file_attachment': >here<,
}
...
class DataInputForm(forms.Form):
attachment = forms.FileField()
class MyView(FormView):
template_name = 'view.html'
form_class = DataInputForm
def get_success_url(self):
return str(
reverse_lazy("milk")
)
def post(self, request, *args, **kwargs):
get_file = request.FILES.get('attachment')
...
k = Fruits()
k.load_data()
return self.render_to_response(context)
I would like to bring the attachment(In fact, get_file) that the user attached to the web class Fruits's >here<
In other words, I would like to save the file(get_file) in DB column (put_file_attachment) by the user's attachment. How can I get a value passed to a request from another class to another class?
I tried to get 'get_file' by creating a MyView object in the Fruit class, but it doesn't work.
Is that possible in this structure or Am I not understanding the concept of request??
The variable must be explicitly passed to the class for it to be available. It's currently in a different scope, so it won't be available.
So, either refactor your Fruits class to take your file as an argument to your constructor (ie, __init__), or pass it in some other way, such as a parameter to your load_data method.
Related
More generally speaking I want add a custom admin Panel to list some related content. To lookup this related content I need to pass the current instance of the model or at least its ID to this panel. How can I do that within these lists in which these admin panels are noted?
Here is my specific example of an ArtistPage. In the editor I would like to add a panel to list WorkPages that are related to this ArtistPage:
from wagtail.models import Page
class ArtistPage(Page):
# ...
content_panels = [
# ...
ListWorksPanel(artist=self), # This doesn’t work
]
The panel itself is defined like that, mostly copied from the HelpPanel:
from wagtail.admin.panels import Panel
class ListWorksPanel(Panel):
def __init__(self, artist="", template="admin/list_works_panel.html", **kwargs,):
super().__init__(**kwargs)
self.artist = artist
self.template = template
def clone_kwargs(self):
kwargs = super().clone_kwargs()
del kwargs["help_text"]
kwargs.update(
artist=self.artist,
template=self.template,
)
return kwargs
class BoundPanel(Panel.BoundPanel):
def __init__(self, panel, instance, request, form):
super().__init__(panel, instance, request, form)
self.template_name = self.panel.template
self.artist = self.panel.artist
This is more a general Python question, I think. I know how to pass "self" in functions. But how does that work here with this class as element of a list? I reckon that the __init__() method of the ArtistPage is the way to go, but I cannot figure out how exactly.
What is the pythonic way of passing "self" to another class?
Update (Solution):
Following #gasman’s aswer, I just added the get_context_data method to the BoundPanel class. The works are accessible in the template of the panel now!
class ListWorksPanel(Panel):
def __init__(self, artist="", template="admin/list_works_panel.html", **kwargs,):
super().__init__(**kwargs)
self.artist = artist
self.template = template
def clone_kwargs(self):
kwargs = super().clone_kwargs()
del kwargs["help_text"]
kwargs.update(
artist=self.artist,
template=self.template,
)
return kwargs
class BoundPanel(Panel.BoundPanel):
def __init__(self, panel, instance, request, form):
super().__init__(panel, instance, request, form)
self.template_name = self.panel.template
self.artist = self.panel.artist
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
context['works'] = self.instance.works.all() # exactly what I needed
return context
The ArtistPage instance is passed to BoundPanel.__init__ as the keyword argument instance. All code that deals with an individual ArtistPage needs to be written inside the BoundPanel class.
When you write ListWorksPanel() as part of a content_panels definition, you're creating a ListWorksPanel instance that then becomes part of the definition of the ArtistPage class. At this point in the code, no actual instance of ArtistPage exists, so there's no self to refer to. Effectively, there's a single ListWorksPanel object shared by all ArtistPage instances that will ever be created.
When the time comes to render the edit form for an individual page, Wagtail calls get_bound_panel on the ListWorksPanel object, passing the page instance along with the form and request objects. (The full process is explained here.) This returns an instance of BoundPanel, which is a template component that performs the final rendering. In this case, you probably want to define a get_context_data method on BoundPanel that does something like context['works'] = self.instance.works.all() - this will then make the variable works available on the template.
here is my view.py
class categAdmin(admin.ModelAdmin):
change_form_template = 'category_forms.html'
list_display = ['title']
model = Category
fields = ['status','title','category_post','body', 'photo',
'url','slider','Gallery','lists','pk_tree','video','maps']
# def render_change_form(self, request, context, **kwargs):
# post = Post.objects.all()
# context['eve'] = post
# return super(categAdmin,self).render_change_form(request, context, **kwargs)
def item_add(request, self, post_id):
tree = post_id
return self.add_view(request, extra_context={'tree': tree})
i am getting error item_add() missing 1 required positional argument: 'request'
You need to swap self and request parameters.
def item_add(self, request, post_id):
tree = post_id
...
Always remember that methods are bound to objects and whenever you call methods, python implicitly passes the self argument(the object on which the method is being called) to the method call, In your example:
class CategAdmin:
def item_add(self, request, post_id):
pass
would be the signature format, note that the self object is the first parameter in the method signature. So when you do
categoryAdmin = CategAdmin()
categoryAdmin.item_add(request,123)
this is what will be called by the python interpreter CategAdmin.item_add(categoryAdmin,request,123)
One more feedback would be to improve your coding style, i.e follow some conventions like always start class names with Capital letter, give meaningful names to class and methods and variables.
This makes your code more readable and via this debugging will be way faster.
Cheers!
I'm trying to define a variable in my view like this:
class PostMessageView(LoginRequiredMixin, generic.TemplateView):
url_redirect = None
def post(self, request, *args, **kwargs):
return redirect(self.url_redirect)
I know this is not the good way, and there are build-in classes for that, but my problem is not here. My problem is about pure Python (I guess). If I make a descendant, I can do it like that, it works:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
url_redirect = 'my_profile_contact_detail'
My problem is when I want to change url_redirect with a dynamic value, like:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
def get_context_data(self, **kwargs):
self.url_redirect = self.request.build_absolute_uri(self.request.path)
Then I get argument of type 'NoneType' is not iterable because, I guess, self.url_redirect doesn't overwrite url_redirect.
How to do it properly in Python?
You can use a property for this:
class ContactDetailView(PostMessageView):
template_name = 'my_home/contact_detail.html'
#property
def url_redirect(self):
return self.request.build_absolute_uri(self.request.path)
This url_redirect method essentially acts like an attribute of the class. Using the decorator version like this will make it a getter only. You can use property as a method instead, if you wanted to make a setter as well.
The issue is that get_context_data() is not called as it should be called by your post() method.
This should work:
def post(self, request, *args, **kwargs):
self.get_context_data()
return redirect(self.url_redirect)
However, get_context_data() is supposed to return a dictionary of data to pass to the template, it is not supposed alter the object state.
I'm using tastypie and I want to create a Resource for a "singleton" non-model object.
For the purposes of this question, let's assume what I want the URL to represent is some system settings that exist in an ini file.
What this means is that...:
The fields I return for this URL will be custom created for this Resource - there is no model that contains this information.
I want a single URL that will return the data, e.g. a GET request on /api/v1/settings.
The returned data should return in a format that is similar to a details URL - i.e., it should not have meta and objects parts. It should just contain the fields from the settings.
It should not be possible to GET a list of such object nor is it possible to perform POST, DELETE or PUT (this part I know how to do, but I'm adding this here for completeness).
Optional: it should play well with tastypie-swagger for API exploration purposes.
I got this to work, but I think my method is kind of ass-backwards, so I want to know what is the common wisdom here. What I tried so far is to override dehydrate and do all the work there. This requires me to override obj_get but leave it empty (which is kind of ugly) and also to remove the need for id in the details url by overriding override_urls.
Is there a better way of doing this?
You should be able to achieve this with the following. Note I haven't actually tested this, so some tweaking may be required. A more rich example can be found in the Tastypie Docs
class SettingsResource(Resource):
value = fields.CharField(attribute='value', help_text='setting value')
class Meta:
resource_name = 'setting'
fields = ['value']
allowed_methods = ['get']
def detail_uri_kwargs(self, bundle_or_obj):
kwargs = {}
return kwargs
def get_object_list(self, request):
return [self.obj_get()]
def obj_get_list(self, request=None, **kwargs):
return [self.obj_get()]
def obj_get(self, request=None, key=None, **kwargs):
setting = SettingObject()
setting.value = 'whatever value'
return setting
The SettingObject must support the getattr and setattr methods. You can use this as a template:
class SettingObject(object):
def __init__(self, initial=None):
self.__dict__['_data'] = {}
if initial:
self.update(initial)
def __getattr__(self, name):
return self._data.get(name, None)
def __setattr__(self, name, value):
self.__dict__['_data'][name] = value
def update(self, other):
for k in other:
self.__setattr__(k, other[k])
def to_dict(self):
return self._data
This sounds like something completely outside of TastyPie's wheelhouse. Why not have a single view somewhere decorated with #require_GET, if you want to control headers, and return an HttpResponse object with the desired payload as application/json?
The fact that your object is a singleton and all other RESTful interactions with it are prohibited suggests that a REST library is the wrong tool for this job.
I am trying to define a base request handling class so that the webapp pages may inherit some basic methods and variable which otherwise would be required to be repeatedly be defined for each page of the application. A sort of similar functionality like django preprocessors. This is my base class from which other pages inherit:
class BasePage(webapp.RequestHandler):
def __init__(self):
self.user = users.get_current_user()
self.template_values = {
'user': self.user,
'environ': self, #I don't like the idea of passing the whole environ object to a template
##The below three functions cannot be executed during _init_ because of absence of self.request
#'openid_providers': self.openid_providers(),
#'logout_url': self.get_logout_url(),
#'request': self.get_request(),
}
##A sort of similar functionality like render_to_response in django
def render_template(self, template_name, values = None, *args, **kwargs):
#PATH is the directory containing the templates
if values:
for value in values: self.template_values[value] = values[value]
self.response.out.write(template.render(PATH+template_name, self.template_values, *args, **kwargs))
##Returns request as the name suggests
def logout_url(self):
return users.create_logout_url(self.request.url)
##Returns request as the name suggests
def request(self):
return request
##Returns openid login urls
def openid_providers(self):
#OPENID_POVIDERS is a list of dictionary
for p in OPENID_PROVIDERS:
p['login_url'] = users.create_login_url(self.request.get('next', '/') , p['name'], p['url'])
return OPENID_PROVIDERS
Everything is working fine except that I cannot pass some variables during initialization as self.request is not available. So for a workaround what I did is pass on whole self variable as a template variable.
Is there some other way to provide the template variables (request, logout_url etc) to the templates?
A much simpler solution than bgporter's is to do the common setup in the initialize method of webapp.RequestHandler. Here's an example from work, where we wanted to add a Django-like is_ajax method to the request object:
class BaseHandler(webapp.RequestHandler):
def initialize(self, request, response):
super(BaseHandler, self).initialize(request, response)
# Add a Django-like is_ajax() method to the request object
request.is_ajax = lambda: \
request.environ.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
This method is called to, uh, initialize each request handler with the current request and response objects, before the appropriate get or post (or whatever) methods are called.
I've solved that problem in my AppEngine code by using the Template Method Pattern
Basically, the base class looks like:
class MyBasePage(webapp.RequestHandler):
def __init__(self):
# common setup/init stuff here,
# omitted for this discussion
def Setup(self):
# request handling setup code needed in both GET/POST methods, like
# checking for user login, getting session cookies, etc.
# omitted for this discussion
def get(self, *args):
self.Setup()
# call the derived class' 'DoGet' method that actually has
# the logic inside it
self.DoGet(*args)
def post(self, *args):
self.Setup()
# call the derived class' 'DoPost' method
self.DoPost(*args)
def DoGet(self, *args):
''' derived classes override this method and
put all of their GET logic inside. Base class does nothing.'''
pass
def DoPost(self, *args):
''' derived classes override this method and
put all of their POST logic inside. Base class does nothing.'''
pass
...your derived classes then mostly just need to worry about the guts of those DoGet() and DoPost() methods.