Django Admin Action using intermediate page - django

I have a model with a lot of fields. I only have a few fields I that I want to be required. So instead of the change list super long, I want to have a short change list then have admin actions that can give predefined subsets of the fields.
The initial action takes me to the correct page but when I submit the form it returns me to whatever page I designate, but doesn't update the fields. I am okay with tearing this down starting over again if needed. I think what I really need to know, what do I put in the action="" portion of the html to have the recursion work properly?
I am using django 1.7. I have to obfuscate a lot of my fields as a cya thing since I am working in a heavily information secure field.
Here is my admin.py
class CredentialAdmin(admin.ModelAdmin):
fields = ['reservedBy','reserveto']
list_display = ['reservedBy','reserveto']
class reserveToFormAdmin(forms.Form):
reservedBy = forms.CharField(widget=forms.Textarea, max_length=50)
reserveto = forms.DateTimeField(widget=forms.DateTimeInput)
def reserveCred(self, request, queryset):
form = None
plural = ''
if 'submit' in request.POST:
form = self.reserveToFormAdmin(request.POST)
for f in form.fields:
print f
print form.is_valid()
print form.errors
if form.is_valid():
reservetos = form.cleaned_data['reserveto']
reservedBys = form.cleaned_data['reservedBy']
print "hello"
count = 0
for cred in queryset:
cred.reserveto = reservetos
cred.reservedBy = reservedByss
cred.save()
count += 1
if count != 1:
plural = 's'
self.message_user(request, "Successfully reserved %s cred%s." % (count, plural))
return HttpResponseRedirect(request.get_full_path(),c)
if not form:
form = self.reserveToFormAdmin(initial={'_selected_action' : request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
return render(request,'admin/reserveCreds.html',{'creds':queryset, 'form':form, 'path':request.get_full_path()})
reserveCred.short_description = "Reserve Selected Creds"
actions = [check_out_a_cred,check_in_a_cred,audit_creds,CompareAudits,reserveCred]
reserveCreds.html
{% extends "admin/base_site.html" %}
{% block content %}
<p>How long and which department to reserver creds:</p>
<form action="{{ path }}" method="post">{% csrf_token %}
{{ form }}
<input type="submit" name="submit" value="submit" />
<input type="button" value = "Cancel" />
</form>
<h2> reserving: </h2>
<ul>
{% for cred in creds %}
<li> {{ cred.userid }} </li>
{% endfor %}
</ul>
{% endblock %}

Related

Can't advance Django paginator after formset submit

[** found a fix, see below **]
I'm having trouble getting Django 2 Paginator to work with a modelformset. There are three models, Place & Hit (one-to-many), and Link. The 'validator' view pages through Place objects 1 at a time, builds a queryset of Hits filtered by the FK placeid. The context sent to the template includes 1) the formset=HitFormSet, 2) a 'records' list with only the one Place object, and 3) the Paginator page.
The template renders the single Place record on the left side, and a scrolling list of Hit forms on the right. The Hit form has two added fields, 'match' (3 radio buttons) and 'flag' (checkbox). The user selects those if one or more Hits match the Place. Upon submitting, a new Link record is created with a placeid, a hitid, and values from the radios and checkbox. Also, a 'reviewed' field in the Place record is set to True.
The code below works to load Place #1, then page through the records - displaying a Place and its Hits. Clicking the Save button creates a new Link record as desired. The problem is that after the save, although the next Page loads on the left, its corresponding hits don't. By displaying pprint(locals()) I can see the correct hits are in the queryset, but the Hit fields in the formset all retain the values from the previous set of forms. The Paginator is advancing and the next Place loads, but not its Hit formset.
I've banged at this for a couple days, read docs, searched, etc. Any ideas?
view.py
def validator(request):
record_list = Place.objects.order_by('placeid').filter(reviewed=False)
paginator = Paginator(record_list, 1)
page = request.GET.get('page')
records = paginator.get_page(page)
count = len(record_list)
context = {
'records': records,
'page': page if request.method == 'GET' else str(int(page)-1)
}
placeid = records[0].placeid
hitid = records[0].hitid
q = Hit.objects.filter(placeid=placeid)
HitFormset = modelformset_factory(
Hit, fields = ['id','hitid', ],form=HitModelForm,extra=0)
formset = HitFormset(request.POST or None, queryset=q)
context['formset'] = formset
if request.method == 'GET':
method = request.method
print('a GET')
else:
if formset.is_valid():
print('formset is valid')
for x in range(len(formset)):
link = Link.objects.create(
placeid = placeid,
hitid = formset[x].cleaned_data['hitid'],
match = formset[x].cleaned_data['match'],
flag = formset[x].cleaned_data['flag'],
)
# flag Place record as reviewed
matchee = get_object_or_404(Place, placeid = placeid)
matchee.reviewed = True
matchee.save()
else:
print('formset is NOT valid')
print(formset.errors)
pprint(locals())
return render(request, 'validator/template.html', context=context)
template.html
{% block content %}
<div class="pagination">
<span class="step-links">
... all standard, works fine
</span>
</div>
{% for record in records %}
{% if records.has_next %}
<!-- <form id="form_related" method="POST" action="" > -->
<form id="form_related" method="POST" action="?page={{ records.next_page_number }}" >
{% else %}
<form id="form_related" method="POST" action="" >
{% endif %}
{% csrf_token %}
{{ formset.management_form }}
<input type="hidden" name="placeid" value="{{ record.placeid }}" />
{% for form in formset %}
<div class="row">
<div class="col-sm-4 id="place-record">
<!-- Place attributes -->
</div>
<div class="col-sm-8" id="hit-forms">
<div id="review">
<span>{{ form.match }} flag: {{ form.flag_geom }}</span>
</div>
<div id="hit">
<!-- Hit attributes -->
</div>
</div>
</div>
{% endfor %}
{% endfor %}
{% endblock%}
The answer (or an answer) turned out to be doing a redirect immediately after the save/POST. The save of Link record removes the Place from the queue, so the page is always "1"
view.py
...
if formset.is_valid():
for x in range(len(formset)):
link = Link.objects.create(
placeid = placeid,
tgnid = formset[x].cleaned_data['tgnid'],
match = formset[x].cleaned_data['match'],
flag_geom = formset[x].cleaned_data['flag_geom'],
)
matchee = get_object_or_404(Place, placeid = placeid)
matchee.reviewed = True
matchee.save()
return redirect('/formset/?page='+page)
else:
print('formset is NOT valid')
print(formset.errors)
...

In django, how to use another model data from function based views

So I have a form that updates a key_instance object with a borrower. Currently my app needs the user to enter the name of the borrower, but I want it to display a dropdown list of data from another model the user model to select from, is there anyway to do this in a class based view? Here are my views.py and my template. What I was thinking is that I would like to use a get_list_or_404 on the user model and display it as a drop down list in the template and use that selection to populate the form field.
I manged to get the dropdown list to display in my template but I'm not sure as to how to save that value in my views.
Does anyone know if this is the right way or if this is doable? Thank you!!
views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(Users)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = UpdateKeyForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required (here we just write it to the model due_back field)
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = form.cleaned_data['borrower']
key_inst.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
form = UpdateKeyForm(initial={'borrower': 'N/A'})
return render(request, 'catalog/keyinstance_request_update.html', {'form': form, 'keyinst':key_inst})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
<table style="display: inline-flex">
{{ form}}
</table>
<select name = 'name'>
{% for name in names %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}
Here is how I manged to do it, Not sure if this is the best 'pythonic' or best practice. Please let me know if it's not.
my views.py
def submit_key_request(request, pk):
"""
View function for renewing a specific keyInstance by admin
"""
key_inst=get_object_or_404(KeyInstance, pk=pk)
names = get_list_or_404(User)
# If this is a POST request then process the Form data
if request.method == 'POST':
name = request.POST['name']
key_inst.is_requested = True
key_inst.status = 'r'
key_inst.date_requested = datetime.date.today()
key_inst.borrower = name
key_inst.save()
return HttpResponseRedirect(reverse('all-available-keys') )
# If this is a GET (or any other method) create the default form.
else:
pass
return render(request, 'catalog/keyinstance_request_update.html', {'keyinst':key_inst, 'names':names})
template
{% extends "base_generic.html" %}
{% block content %}
<div class="wrapper">
<div class="centered"> <h1>Request Keys For Room: {{keyinst.roomkey}}</h1></div>
<div class="square-box">
<div class="square-content">
<form action="" method="post" >
{% csrf_token %}
</br>
<select name = 'name' required>
{% for key in names %}
<option value="{{ key }}">{{ key }}</option>
{% endfor %}
</select>
<p>
(Please use their login name i.e. <b>{{ user.get_username }}</b>)
</p>
<p><input required id="checkBox" type="checkbox" onclick="validate()"> I accept the terms and conditions</p>
<p id="text" style="display:none">You Have Agreed To the Terms and Conditions</p>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}

django post returning token not post data

I'm making a post from a webpage that is generated dynamically from Django DB content. The data is put into a combo box and is supposed to send the customer id back to Django so I can parse some more queries with it and then display only the customers data that was selected.
However instead of retruning my post data it returns the csrf token instead of the posted data.
<form method='post' action='' id='customers'>
{% csrf_token %}
<select>
{% for item in customer %}
<option value={{ item.customer_id }} name='customer_id'>{{ item.customer_name }}</option>
{% endfor %}
<input type = 'submit' value='Edit'>
</select>
{{ post }}
{{post.customer_id}} #doesnt work either.
this is what it returns:
<QueryDict: {u'csrfmiddlewaretoken': [u'CpzKrwmZsmfiiNHngNWDFSNxqUoBykYO']}>
def portal(request):
customers = Customer.objects.all()
if request.method == 'POST':
vm_groups = Vm_group.objects.all()
vms = Vm.objects.all()
selected_customer = request.POST.copy()
#selected_customer_id = selected_customer['customer_id']
post = selected_customer
context = Context({'customer': customers, 'vm_groups':vm_groups, 'vms':vms, 'post':post,
})
return render(request, 'portal.html', context)
else:
context = Context({'customer': customers,})
return render(request, 'portal.html', context)
Also how would I get just the customer_id from the returned data once it works correctly?
selected_customer = request.POST.copy()
selected_customer_id = selected_customer['customer_id']
Like so?
You need to put the name attribute on the select element, not the option.
But you should probably be using Django's forms framework, anyway.
As #Daniel told that, You need to add name field to select tag not for option tag,
<select name='customer_id'>
{% for item in customer %}
<option value={{ item.customer_id }} >{{ item.customer_name }}</option>
{% endfor %}
<input type = 'submit' value='Edit'>
</select>

ChoiceField form is not valid and returns "too many values to unpack"

So, I am having this form where I display a list of all my app models in a drop-down list and expect from the user to choose one so as to display its fields. Below is my form and the models() method, which creates the list of models to be passed as argument to my ChoiceField.
*forms.py*
class dbForm(forms.Form):
model_classes_field = forms.ChoiceField(choices=models(), required=True,)
def models():
apps = get_app('Directories')
m_id = 0
for model in get_models(apps):
m_id += 1
model_classes.append({
'model_name': model._meta.verbose_name,
'model_id': m_id,
'model_table': model._meta.db_table,
'model_object': model.objects.all()
})
return model_classes
In my views.py I tried handling the POST data but unfortunately for some reason the form was not valid and I couldn't manipulate any data. Furthermore form.errors does not display anything at all.
*views.py*
def index(request):
if request.method == 'POST': # If the form has been submitted...
form = dbForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
model_classes_field = form.cleaned_data['model_classes_field']
return HttpResponseRedirect('/list/') # Redirect after POST
else:
print "form: ", form.errors
else:
form = dbForm() # An unbound form
print "form: ", form.errors
print "not valid form"
return render(request, 'Directories/index.html', {
'form': form,
})
Furthermore, in the template whenever i try to submit the form it returns an error message "too many values to unpack" and does not redirect me to the next template (list.html).
*index.html*
{% block content%}
<div id="content" align="center">
<h2> Welcome! this is Directories app! </h2>
<form action="" method="post">{% csrf_token %}
{{ form.model_classes_field.errors }}
<label for="id_model_classes_field">Choose from one of the existing tables:</label>
<select id="id_model_classes_field" name="model_classes_field">
{% for choice in form.fields.model_classes_field.choices %}
<option name="m_id" value="{{ choice.model_table }}">{{choice.model_id}}: {{choice.model_name}}</option>
{% endfor %}
</select> <br />
<input type="submit" value="Change" name="_change" />
</form>
</div>
<div id="bottom">
</div>
{% endblock %}
The only workaround i found on this is to fill the form action with the template to be redirected at (i.e. action = "list") instead of doing it in the views with return HttpResponseRedirect('/list/') . However, I believe that this does not solve the issue since still the form is not valid and i cannot process data with form.cleaned_data. It's peculiar though that the post data is sent even though the form is not valid.
*
EDIT: Solution
I changed my models() method as such:
def models():
apps = get_app('Directories')
for model in get_models(apps):
model_classes.append( (model._meta.verbose_name, model._meta.db_table), )
return model_classes
so I included a tuple as instructed by #Rohan and after making a slight alteration to my index.html:
...
{% for choice in form.fields.model_classes_field.choices %}
<option name="m_id" value="{{choice.0}}">{{choice.0}}</option>
{% endfor %}
...
form is valid and can now process my data.
*
Value of choices should be list/tuple containing items with exactly 2 elements. But you are creating list of dicts which might be causing the issue.
for model in get_models(apps):
m_id += 1
model_classes.append({
'model_name': model._meta.verbose_name,
'model_id': m_id,
'model_table': model._meta.db_table,
'model_object': model.objects.all()
})
So you may want to update models() method to return appropriate list/tuple.

Django URL user id versus userprofile id problem

I have a mini comunity where each user can search and find another user's profile.
Userprofile is a class model, indexed differently compared to user model class (user id is not equal to userprofile id).
But I cannot see a user profile by typing in the URL the corresponding id. I only see the profile of the currently logged in user.
Why is that?
I'd also want to have in my URL the username (a primary key of the user table also) and NOT the id (a number).
The guilty part of the code is:
What can I replace that request.user with so that it will actually display the user I searched for, and not the currently logged in?
def profile_view(request, id):
u = UserProfile.objects.get(pk=id)
cv = UserProfile.objects.filter(created_by = request.user)
blog = New.objects.filter(created_by = request.user)
return render_to_response('profile/publicProfile.html',
{
'u':u,
'cv':cv,
'blog':blog,
},
context_instance=RequestContext(request))
In file urls.py (of the accounts app):
url(r'^profile_view/(?P<id>\d+)/$',
profile_view,
name='profile_view'),
And in template:
<h3>Recent Entries:</h3>
{% load pagination_tags %}
{% autopaginate list 10 %}
{% paginate %}
{% for object in list %}
<li>{{ object.post }} <br />
Voted: {{ vote.count }} times.<br />
{% for reply in object.reply_set.all %}
{{ reply.reply }} <br />
{% endfor %}
<a href=''> {{ object.created_by }}</a> <br />
{{object.date}} <br />
Vote this
Comment </li>
{% endfor %}
Replace
cv = UserProfile.objects.filter(created_by = request.user)
blog = New.objects.filter(created_by = request.user)
With
#u is UserProfile.objects.get(pk=id)
cv = UserProfile.objects.filter(created_by = u)
blog = New.objects.filter(created_by = u)