Django and users entering data - django

I am building a webapp which will be used by a company to carry out their daily operations. Things like sending invoices, tracking accounts receivable, tracking inventory (and therefore products). I have several models set up in my various apps to handle the different parts of the web-app. I will also be setting up permissions so that managers can edit more fields than, say, an office assistant.
This brings me to my question. How can I show all fields of a model and have some that can be edited and some that cannot be edited, and still save the model instance?
For example, I have a systems model for tracking systems (we install irrigation systems). The system ID is the primary key, and it is important for the user to see. However, they cannot change that ID since it would mess things up. Now, I have a view for displaying my models via a form using the "form.as_table". This is efficient, but merely spits out all the model fields with input fields filling in the values stored for that model instance. This includes the systemID field which should not be editable.
Because I don't want the user to edit the systemID field, I tried making it just a label within the html form, but django complains. Here's some code:
my model (not all of it, but some of it):
class System(models.Model):
systemID = models.CharField(max_length=10, primary_key=True, verbose_name = 'System ID')
systemOwner = models.ForeignKey (System_Owner)
installDate = models.DateField()
projectManager = models.ForeignKey(Employee, blank=True, null=True)
#more fields....
Then, my view for a specific model instance:
def system_details(request, systemID):
if request.method == 'POST':
sysEdit = System.objects.get(pk=systemID)
form = System_Form(request.POST, instance=sysEdit)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
sysView = System.objects.get(pk=systemID)
form = System_Form(instance=sysView)
return render_to_response('pages/systems/system_details.html', {'form': form}, context_instance=RequestContext(request))
Now the html page which displays the form:
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Changes">
<input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
</form>
So, what I am thinking of doing is having two functions for the html. One is a form for displaying only those fields the user can edit, and the other is for just displaying the content of the field (the systemID). Then, in the view, when I want to save the changes the user made, I would do:
sysValues = System.objects.get(pk=SystemID)
form.save(commit = false)
form.pk = sysValues.sysValues.pk (or whatever the code is to assign the sysValues.pk to form.pk)
Is there an easier way to do this or would this be the best?
Thanks

One thing you can do is exclude the field you don't need in your form:
class System_Form(forms.ModelForm):
class Meta:
exclude = ('systemID',)
The other is to use read-only fields: http://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields as #DTing suggessted

To make a field read only you can set the widget readonly attribute to True.
using your example:
class System_Form(ModelForm):
def __init__(self, *args, **kwargs):
super(System_Form, self).__init__(*args, **kwargs)
self.fields['systemID'].widget.attrs['readonly'] = True
class Meta:
model = System
or exclude the fields using exclude or fields in the class Meta of your form and display it in your template if desired like so:
forms.py
class System_Form(ModelForms):
class Meta:
model = System
exclude = ('systemID',)
views.py
def some_view(request, system_id):
system = System.objects.get(pk=system_id)
if request.method == 'POST':
form = System_Form(request.POST, instance=system)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = System_Form(instance=system)
context = { 'system':system,
'form':form, }
return render_to_response('some_template.html', context,
context_instance=RequestContext(request))
some_template.html
<p>make changes for {{ system }} with ID {{ system.systemID }}</p>
<form method='post'>
{{ form.as_p }}
<input type='submit' value='Submit'>
</form>

Related

Passing data to form (from POST and url)

I'm not sure how to pass data from a given object (profile) and from form (request.POST) to one model (many to many relation).
I have two models: Profile and Keyword. Users can define many profiles (for different subjects) and in each profile they can define many keywords (used later by a crawler). It is possible that the same keyword is in many profiles, and one profile can have many keywords.
Now, I have a view adding new profile to user, and in next step I want to add view adding keyword/keywords to this particular profile.
I'm passing a parameter foreign key - profile_id - via url, and I have build form from my model Keyword. Now I have problem with passing both of them to my function.
models.py
class Profiles (models.Model):
id_profile = models.AutoField(primary_key=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=45)
description = models.TextField(max_length=120, default=' ')
class Keywords (models.Model):
id_keyword = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
id_profile = models.ManyToManyField(Profiles)
template
<button class="btn btn-outline-danger" type="submit"> new keyword </button>
urls.py
path('profile_detail/<int:pk>/', users_view.ProfileDetailView.as_view(), name = 'profile_detail'),
path('new_keyword/<profile_id>/', users_view.NewKeyword, name = 'new_keyword'),
views.py
def newKeyword(request):
form = NewKeyword(request.POST)
if request.method == 'POST':
form.is_valid()
form.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
Now I have
__init__() got an unexpected keyword argument 'profile_id'
I understand that I have to somehow overwrite init() to accept profile_id, but I'm not sure how.
ok, thank you for you answer. I have changed my code, but now I have different problem:The Keywords could not be created because the data didn't validate.
def newKeyword(request):
context = {}
context['id_profile'] = request.POST.get('id_profile', None)
form = NewKeyword(request.POST or None)
if request.method == 'POST':
form.is_valid()
obj = form.save(commit=False)
obj.save()
obj.id_profile.add(context['id_profile'])
obj.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
and template in previous page:
<form method="POST" name="newkeyword" value='keyword'>
<fieldset class="form-group">
<legend class="border-bottom mb-4"> New keyword </legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-danger" type="submit">create</button>
</div>
</form>
Looks like after clicking a button on page profile (in order to go to view: creating keyword) I use method POST to send parameter, hence my 'If statement' runs...
You are trying to retrieve GET data.
def newKeyword(request, profile_id):
and you can retrieve that parameter with this way but for POST data,
you can use;
request.GET.get('profile_id') # GET method
request.POST.get('profile_id') # POST method
and you don't need to edit url for this.
related question:
Passing variable from django template to view

Beginners: Dynamic filtering of foreign key field in Django CreateView form

Models:
class Instructional_Cycle(models.Model):
date_started = models.DateField()
date_finished = models.DateField()
standard_tested = models.OneToOneField(Standard, on_delete=models.CASCADE)
class Standard(models.Model):
subject = models.CharField(max_length=14, choices=subjects)
grade_level = models.IntegerField(choices=gradeLevels)
descriptor = models.CharField(max_length=15)
description = models.TextField()
essential_status = models.BooleanField(default=False)
View:
class CycleCreateView(CreateView):
model = Instructional_Cycle
template_name = 'cycle_new.html'
fields = '__all__'
success_url = reverse_lazy('student_progress:cycles')
Template:
<!-- student_progress/cycle_new.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Add a new instructional cycle:</h1>
<form action="{% url 'student_progress:cycle_new' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">add cycle</button>
</form>
{% endblock content %}
The problem I'm having with this form is that the dropdown to select Instructional_Cycle.standard_tested has literally 1000 records from Standard. There's no way that the user can scroll through all of those and find the one record they want.
What I need is some way to click a link and filter the dropdown list by subject or grade_level and/or a search box, similar to what's achieved on the admin side by creating a custom admin model in admin.py like so:
class StandardAdmin(admin.ModelAdmin):
list_display = ('descriptor', 'description', 'essential_status')
list_filter = ('subject', 'grade_level', 'essential_status')
search_fields = ('descriptor',)
inlines = [MilestoneInLine]
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
try:
search_term_as_int = int(search_term)
except ValueError:
pass
else:
queryset |= self.model.objects.filter(age=search_term_as_int)
return queryset, use_distinct
Please "dumb it down" for this newbie. I just finished working through Django for Beginners, and my conceptual model of how this all fits together is still full of holes. Please assume that I know hardly anything. Thanks!
That amount of reactive work on one page will require you to be comfortable with Javascript, Ajax, etc. If that is the case, there are a number of approaches you could take that let you refresh the form with the desired options.
Alternatively, you could ask the user for the necessary data one step earlier in the process and let Django build the correct form for you in the first place by overriding the form's default queryset.
You should look into using something like django-ajax-select. https://github.com/crucialfelix/django-ajax-selects

quick fill forms in django

Sorry I have a really basic question while learning Django and could not find an easy answer.
My model is :
class Entry(models.Model):
name = models.CharField(max_length=200)
type = models.CharField(max_length= 200)
date = models.DateTimeField(auto_now= False, auto_now_add=True)
updated = models.DateTimeField(auto_now= True, auto_now_add= False)
description = models.TextField()
And so my general form implementation is :
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['name','type', 'description']
views:
def add(request):
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return HttpResponseRedirect('/')
else:
form = EntryForm()
return render(request, "form.html", {'form': form})
I want to add a quick fill button next to add button (that calls above view ) where the name and type is statically filled in the object and only textbox appears for description field.
I could not find a way to statically assign the values to my field in Django.
I had tried creating a different HTML file ( quickform.html) but {{form.as_p}} will put all the fields.
my forms.html is
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn btn-success" type='submit'>Submit</button>
</form>
what would be the best way to add a quick link to my index page where the name ( is auto-filled to the "general"+str(id)) and type is auto-filled to "general") is auto-filled and does not appear in the form page
In my opinion the easiest way to do such a thing is to add on click event to your second button then write a simple javascript function the fills the elements you want with static values.

Django - Can't Initialize Selected Radio Button when using ModelChoiceField with RadioSelect Widget

I have a form that I want to be pre-populated with data from a model instance when a user is attempting to update an existing database record. When the form is rendered none of the radio buttons pre-selected even though a model instance has been passed to the ModelForm. In a much larger form than listed below, all of the fields except the radio buttons are pre-populated with the correct data from the model instance. How do I get the correct radio buttons pre-selected?
My Models:
class TicketType(models.Model):
type = models.CharField(max_length=15, unique=True)
def __unicode__(self):
return self.type.title()
class TestTicket(models.Model):
ticket_type = models.ForeignKey(TicketType, to_field='type')
My Form
class TestTicketForm(ModelForm):
ticket_type = ModelChoiceField(TicketType.objects.all(),
widget=RadioSelect,
empty_label=None)
class Meta:
model = TestTicket
fields = ['ticket_type']
My View
def test_ticket_update(request, ticket_num=None):
# initialize an update flag to distinguish between a request
# to add a new ticket or an update to an existing ticket.
update_requested = False
ticket_instance = None
if ticket_num:
# This is a request to update a specific ticket.
# Attempt to retrieve the ticket or show a 404 error.
# If a ticket is retrieved, it is locked for editing
# by using 'select_for_update()' to prevent a race condition.
ticket_instance = get_object_or_404(
TestTicket.objects.select_for_update(),pk=ticket_num)
update_requested = True
if request.method == 'POST':
form = TestTicketForm(request.POST, instance=ticket_instance)
if form.is_valid():
ticket = form.save(commit=False)
ticket.save()
return HttpResponseRedirect('/tickets/')
else:
if update_requested:
# This is a requested to update an existing ticket.
# Bind the ticket data to a form.
form = TestTicketForm(instance=ticket_instance)
else:
form = TestTicketForm()
return render(request, 'ticket_tracker/ticket_update.html',
{ 'form': form, 'ticket': ticket_instance})
My Template
{% block content %}
<div class="container">
<form action="/tickets/test-ticket/{{ ticket.id }}/" method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="form-group">
<button type="submit">Save</button>
</div>
</form>
</div>
{% endblock content %}
It appears this has been answered here before. In my model I used the to_field argument in the creation of the ForeignKey field, but the ModelChoiceField is expecting to use the id when the 'initial' value is passed to it. There are several options to fix this in my example including:
Remove the to_field parameter from the ForeignKey field in the model.
When creating the form instance in the view, set the 'initial' parameter for the field using the field's id from the model instance, e.g.,
form = TestTicketForm(request.POST,
instance=ticket_instance,
initial={'ticket_type': instance.ticket_type.id)
Set the form field's initial value in the forms __init__() method. Again this uses the field's id from the model instance. For example:
class TestTicketForm(ModelForm):
ticket_type = ModelChoiceField(TicketType.objects.all(),
widget=RadioSelect,
empty_label=None)
def __init__(self, *args, **kwargs):
super(TestTicketForm, self).__init__(*args, **kwargs)
if self.instance is not None:
self.initial['ticket_type'] = self.instance.ticket_type.id
Option #1 above would require a schema and data migrations in my database. Options #2 and #3 are similar but I chose option #3 since it makes my view code slightly cleaner.

Valid django form not saving

In my view for editing friends I'm checking if form is valid, and then save the form. But somehow the data are not updated. Why my updated form is not saved ? It is 100% valid, since I've checked it earlier.
My form :
class FriendForm(forms.ModelForm):
first_name = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="First name")
last_name = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="Last name")
pid = forms.RegexField(regex=r'^\d{11}', max_length=11 ,widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)))
image = forms.ImageField(label="Image", required=False)
street = forms.CharField(widget=forms.TextInput(attrs=dict(attrs_dict, maxlength=50)), label="Street")
number = forms.CharField(widget=forms.TextInput, label="House/flat number")
code = forms.RegexField(regex=r'^\d{2}[-]\d{3}', max_length=6, widget=forms.TextInput(attrs=attrs_dict), label="Postal code")
city = forms.CharField(widget=forms.TextInput, label="City")
The view :
def edit_friend(request, id):
userprofile = UserProfile.objects.get(user=request.user)
friend = get_object_or_404(Friend, id=id)
if friend.friend_of.id!=userprofile.id:
raise Http404
if request.method == 'POST':
form = FriendForm(request.POST, request.FILES, instance=friend)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('user_profile',))
else:
form = FriendForm(instance=friend)
return render_to_response('user/data_operations/edit_friend.html', {
'form':form, 'user':request.user,
}, context_instance=RequestContext(request))
Template :
<form method="post" action="." enctype="multipart/form-data">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="submit" class="submit" name="submit" value="Save" />
</td>
</tr>
</table>
</form>
I'd need to see the full code for your form to really answer this (can you include it?) but here are some initial thoughts:
Is FriendForm a subclass of django.forms.ModelForm? If so, there's no need to override the save method -- especially since all you're doing here is getting the returned new object, saving it (again), and returning it (again) -- additional processing with no additional benefit.
If FriendForm isn't a subclass of ModelForm, how is it bound to database data? What class is it inheriting from?
UPDATE:
ModelForms aren't connected directly to the database -- they are a shortcut for creating HTML forms for interacting with database models -- i.e. classes inheriting from django.models.Model. You do that by creating a Meta class within your ModelForm.
With a ModelForm, you don't need to manually specify the fields (django does that automatically for you) unless you want to override specific behaviors. You do have to tell django which database Model you're like to use. If you've already defined a database Model, use it; if not, try this:
# in models.py
from django import models
class Friend(models.Model):
first_name = models.CharField( "see <http://docs.djangoproject.com/en/dev/ref/models/fields/> to adjust your syntax" )
... your other fields ...
# in forms.py
from django.forms import ModelForm
from my_project.my_app.models import Friend
class FriendForm(ModelForm):
class Meta:
model = Friend
That's it! Now your FriendForm should work properly. See http://docs.djangoproject.com/en/dev/topics/forms/modelforms/ for more information on using ModelForms.
Alternatively, you don't need to use a ModelForm at all. You could add the following save method to your existing FriendForm:
def save(self):
if self.is_valid():
from myproject.myapp.models import Friend
new_friend = Friend(**self.cleaned_data)
new_friend.save()
return new_friend
... the essence here is that you're importing the Friend model, which encapsulates the database storage behaviors. This is basically what the ModelForm creates automatically for you, but not as robust -- so I recommend using ModelForm.
sometimes, if you add decorators like #transaction.commit_on_success during development, it prevents saving the form if there is even a single error.
I think you may be overriding your model fields with those form fields.
check this. Normally when you have a modelform all you need to do is define a meta class and pass the extra settings you need for that particular form (rendered fields, their widgets, their labels, etc).
You should set action attribute for your form tag. And set url name in urls.py.
Like <form action="{% url 'edit_friend' %}" method="post">
urls.py:
(...'/some-edit-url/', views.edit_friend, name='edit_friend'),