Adding an extra variable to a column in django-tables2 - django

Trying to build a Django (1.4) site that has some pages that can be either loaded in a popup or not. Some of these pages contain a listview, implemented in Django-tables2
When a page is loaded as a popup, an extra URL parameter is added; for example
/backoffice/popup/articlegroups/ is the same page as /backoffice/articlegroups/ , but shown as a popup.
My question is how to add this extra piece of info (popup or not) to the LinkColumns in Django-tables2, because the links to the edit-page, also need to have this information.
Django-tables2 has an accessor, which can be used to access properties in the queryset, but I need to add an extra piece of data, outside of the queryset. I have seen that adding extra data to an existing dataset is tricky at best, also, that doesn't feel very clean.
I was wondering if there isn't a simple way to add extra data to the tables or columns class, I have tried looking in the table.meta class as well, but to no avail.
My code is as follows:
TABLES.PY
class ArticlegroupTable(tables.Table):
artg_name = LinkIfAuthorizedColumn(
'ArticlegroupUpdate',
args=["popup", A('pk')],
edit_perm="articles.maintenance",
)
This ofcourse works, but it is adding the "popup" arugument as a fixed string as you can see...
class ArticlegroupTable(tables.Table):
artg_name = LinkIfAuthorizedColumn(
'ArticlegroupUpdate',
args=[A('popup'), A('pk')],
edit_perm="articles.maintenance",
)
This does not work, because there isn't a "popup" property in the queryset...
VIEWS.PY
def get_context_data(self, ** kwargs):
# get context data to be passed to the respective templates
context = super(ArticlegroupSearch, self).get_context_data(**kwargs)
data = self.get_queryset()
table = ArticlegroupTable(data, self.request)
RequestConfig(self.request, paginate={
"per_page": 5,
}).configure(table)
context.update({'table': table})
if 'popup' in self.kwargs:
context.update({'popup': self.kwargs['popup']})
return context
It seems that this is not a very far-fetched scenario (adding a URL parameter to a table/column in tables2), so I was wondering if anyone knows of a simple way to do so.
Thanks,
Erik

If you're after a quick hack, just implement the table's __init__ method and add the popup arg to the LinkColumns dynamically:
class ArticlegroupTable(tables.Table):
def __init__(self, *args, **kwargs):
if kwargs.pop("popup", False):
for column in self.base_columns.values():
if isinstance(column, tables.LinkColumn):
column.args.insert(0, "popup")
super(Table, self).__init__(*args, **kwargs)
# …
Then in your view pass in a popup argument:
def get_context_data(self, ** kwargs):
# get context data to be passed to the respective templates
context = super(ArticlegroupSearch, self).get_context_data(**kwargs)
data = self.get_queryset()
popup = self.kwargs.get('popup')
table = ArticlegroupTable(data, self.request, popup=popup)
RequestConfig(self.request, paginate={
"per_page": 5,
}).configure(table)
context.update({'table': table, 'popup': popup})
return context

Related

Django-filters resets after using the UpdateView

I have a Model with a lot of entries, so I'm using django-filters to filter the model, I initially load an empty table and from there I use the filter to view the items.
Everything works fine, the page loads initially with no entry, after I filter, django shows the correct items.
The Url gets a parameter: /retetabloc/?acordcadru=532(532 is the filter) but when I try to update an entry, the filter resets(the parameter is still in the URL) and the whole db is loaded.
I don't quite understand how to pass the filter parameter to the RetetaBlocUpdate, so that after the update is done it returns to the filtered items like in the ListView.
views.py
class RetetaBlocListview(LoginRequiredMixin, CoreListView):
model = RetetaBloc
def get_queryset(self, *args, **kwargs):
pdb.set_trace()
acordcadru = self.request.GET.get("acordcadru")
queryset = RetetaBloc.objects.filter(acordcadru=acordcadru)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = RetetaBlocFilter(self.request.GET, queryset=self.get_queryset())
pdb.set_trace()
return context
class RetetaBlocUpdate(LoginRequiredMixin, AjaxUpdateView):
model = RetetaBloc
form_class = RetetaBlocForm
Thank you.
If you'd like filters to be remembered you could add them to a session variable instead. That way filters would be recalled even if they didn't go back directly from the update page (and you'd wouldn't have redundant URL querystring on pages where they weren't needed).
Something like:
def get_queryset(self, *args, **kwargs):
pdb.set_trace()
#check for new filter in URL first
acordcadru = self.request.GET.get("acordcadru")
#if nothing check for session variable
if not acordcadru:
acordcadru = self.request.session.get('acordcadru')
#if something in URL querystring, set it in session variable
else:
self.request.session['acordcadru'] = acordcadru
queryset = RetetaBloc.objects.filter(acordcadru=acordcadru)
return queryset

How do I use formset data as initial data in my forms? Error is ' The inline value did not match the parent instance.'

I have a formset that I use to collect some data. I also want to use that same data as the initial data to a different formset. In order to do this, I have a form that looks like...
def __init__(self, *args, **kwargs,):
dropdown = kwargs.pop('dropdown', None)
super(TeamFormSet, self).__init__(*args, **kwargs)
self.queryset = Player.objects.filter(team_pk=dropdown)
After a bunch of searching and massaging....I've gotten this to work exactly how I want. The challenge I'm having now is when I'm loading this form, I am getting an error that says The inline value did not match the parent instance. I've done some research on the querysets and formsets and I understand django knows how this data was created originally, but I am trying to essentially use the values as a starting point for this form.
I have seen that get_initial might be available for formsets. Is there some other way I should be getting the data rather than the way I am going about it above? The data is dynamic, and since I've already captured it in another part of the system, I can't see why I shouldn't be able to use the values at a minimum as a starting point for a different form.
After a lot of trial and error, I was able to figure out my approach. I simply needed to override GET.
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
form_class = self.get_form_class()
form = self.get_form(form_class)
dropdown = self.kwargs["pk"]
contacts = MyModel.objects.filter(company_pk=dropdown)

adding detail information to django listview object in template

I have a listview in which I'm hoping to insert additional details about the object (activity duration and average power) in the same row as the link to the object detail (the best way to describe it would be that I want some detailview attributes inserted into the listview). At the moment, the best I can achieve is a separate context dictionary listed below the object_list, as shown in this screen shot:
And the following is my listview:
class RideDataListView(LoginRequiredMixin, ListView):
model = RideData
context_object_name='object_list'
template_name='PMC/ridedata_list.html'
def get_queryset(self):
queryset = super(RideDataListView, self).get_queryset()
return queryset
def get_context_data(self, *args, **kwargs):
model = RideData
context = super(RideDataListView, self).get_context_data(*args, **kwargs)
records = list(RideData.objects.all().values())
actdict2={}
id=[]
ap=[]
actdur=[]
for record in records:
actdf=pd.DataFrame.from_dict(record)
id.append(actdf['id'].iloc[0])
ap.append(actdf['watts'].mean())
actdur.append(str(timedelta(seconds=len(actdf['time']))))
actdf2=pd.DataFrame()
actdf2['id']=id
actdf2['ap']=ap
actdf2['actdur']=actdur
actdict2=actdf2.to_dict('records')
context['actdict']=actdict2
context['actdur']=actdur
return context
What I haven't been able to nail down in my research is if there is a way to either a) annotate the queryset with stuff from context or b) loop through the context dictionary 'actdict' within the object_list loop (doesn't seem possible based on some attempts) or c) include individual lists (ap and actdur as additions to to query. Just curious for some additional leads to add some more object detail to the basic listview.
Your context is intended to contain your data, but the way it is displayed rely on the HTML template you will use : https://docs.djangoproject.com/en/3.0/topics/templates/
The actual solution to this was to add to queryset object within def get_queryset
def get_queryset(self):
queryset = super(RideDataListView, self).get_queryset()
for obj in queryset:
record=list(obj.watts)
actdf=pd.DataFrame()
actdf['watts']=record
obj.actdur=str(timedelta(seconds=len(actdf['watts'])))
obj.ap=actdf['watts'].mean()
return queryset
This returned the additional summary information I wanted to include in the listview that is also used in detailview

How to know what context data returned by any GCBV in django?

Is there any way to know the dictionary returned by any GCBV other than looking at the code.
context_dict
I had to look at the code at django.views.generic.list to know that ListView returns this context dictionary.
Is there any other fast way to know
As per Django Docs, the default is object_list, but can be set using
context_object_name = 'your_context_name'
If you want to add some other context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['your_other_context_name'] = Model.objects.all() # or whatever you want to query
return context

Django Admin filtering ForeignKey dropdown based on another field

I have 3 Models:
class FileType(models.Model):
name=models.CharField(max_length=128)
class ManagedFile(models.Model):
type = models.ForeignKey(FileType)
content = models.FileField(upload_to=path_maker)
class Tag(models.Model):
type = models.ForeignKey(FileType)
m_file = models.ForeignKey(ManagedFile)
def clean(self):
if self.m_file is None:
return
if self.type != self.m_file.type:
raise ValidationError("File type does not match Tag type")
When select an m_file for a tag, the m_files type MUST match the Tags type. This is all well and good, but the admin drop down for Tag.m_file shows files of all types, regardless of the Tag's type. This is Confusing to users.
There seem to me a number of ways to filter the drop down statically. So if I wanted to say that we will never let the user see Type.pk=1 in the dropdown, I can to that. But there does not seem to be a way to filter on m_file.Type == Self.Type
It is actually quite easy to create your admin form classes dynamically. Something like this should work:
def tagform_factory(filetype):
class TagForm(forms.ModelForm):
m_file = forms.ModelChoiceField(
queryset=ManagedFile.objects.filter(type=filetype)
)
return TagForm
class TagAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if obj is not None and obj.type is not None:
kwargs['form'] = tagform_factory(obj.type)
return super(TagAdmin, self).get_form(request, obj, **kwargs)
Note that the get_form method is responsible for building the form class, not the form instance. It is badly named, IMHO.
However, you still need to decide what to do for forms that are used to add new tags, rather than edit existing ones. In that case you do not yet have a type to which you can restrict the dropdown. Maybe there is actually a data modeling problem lurking here? Do you really need the type field on the Tag model? Maybe it should just be removed?
Try overriding formfield_for_foreignkey() in the admin. I'm not 100% it does what you want it to do, but if not it should get you started:
class TagAdmin(admin.ModelAdmin):
...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
kwargs['queryset'] = Tag.objects.filter(type=self.type)