Extending Django User model and makign registration view - django

I'm trying to extend the User model in Django using inheritance. Here's my model:
class Paciente (User):
Carteira = models.CharField(max_length=50,
verbose_name="Num Carteira Plano Saude",unique=True)
Convenio = models.ForeignKey(Operadora,'Descricao', related_name="convenio",
verbose_name="Convenio",unique=True)
DDD = models.CharField(max_length=2, verbose_name="DDD")
Telefone = models.CharField(max_length=10, verbose_name="Telefone")
And here's my view:
def paciente_register(request):
PacienteFormSet = modelformset_factory(Paciente)
if request.method == 'POST':
form = PacienteFormSet(request.POST or None,
request.FILES or None, queryset=Paciente.objects.none(),
initial=[{
'is_staff': False,
'is_active': True,
'is_superuser': False,
'date_joined': datetime.now(),
'last_login': datetime.now()
}]
) #incluir
if form.is_valid():
instance = form.save(commit=False) #pre-salva o registro
for logvalue in instance: # repoe os valores 'hidden' de log
logvalue.is_staff = False
logvalue.is_active = True
logvalue.is_superuser = False
logvalue.date_joined = datetime.now()
logvalue.last_login = datetime.now()
for reform in instance: # salva no banco
reform.save()
else:
return HttpResponseRedirect('/erro/')
else:
form = PacienteFormSet(queryset=Paciente.objects.none(),
initial=[{
'is_staff':False,
'is_active': True,
'is_superuser': False,
'date_joined': datetime.now(),
'last_login': datetime.now()
}]
) #incluir
return render_to_response('register.html', {'form': form,}, context_instance=RequestContext(request))
And, last but not least, my template:
<form enctype="multipart/form-data" method="POST" action="/paciente_register/" >{% csrf_token %}
{{ form.management_form }}
{% for formy in form.forms %}
{{ formy.username.label_tag}}:{{formy.username}}<br>
{{ formy.password.label_tag}}:{{formy.password}}<br>
{{ formy.Carteira.label_tag}}:{{formy.Carteira}}<br>
{{ formy.Convenio.label_tag}}:{{formy.Convenio}}<br>
{{ formy.DDD.label_tag}}:{{formy.DDD}}<br>
{{ formy.Telefone.label_tag}}:{{formy.Telefone}}<br>
{{formy.is_staff.as_hidden}}
{{formy.is_active.as_hidden}}
{{formy.is_superuser.as_hidden}}
{{formy.date_joined.as_hidden}}
{{formy.last_login.as_hidden}}
{% endfor %}
<input type="submit" name="submit" value="Salvar" />
</form>
The user is supposed to register himself, but he will only have access to edit some of the fields, because the others I am setting inside the view and in the template, I am hiding them.
But the thing is that my form is not validating. I did a lot of research, but just a few examples over the internet use inheritance to extend User model. And this ones, does not show how would be the view that makes the registration of the user inside the application.
Any help would be very appreciated! :)

The problem was on the view, after all. I was forcing the fields "date_joined" and "last_login" to receive datetime.now() because I was following the Django admin template. Sadly, this wasn't correct and everytime I tried to add a user, it was jumping to the error template, which means that the form wasn't filled properly. The solution was to make those fields receive date.today() and now it works fine.
I think this model inheritance is still not working very good (Python 2.7 and Django 1.3). When you use it in your models, be sure of what kind of field Django is creating inside your database, and this will save you a lot of trouble.

In general, I think the model inheritance is working fine and without significant issues.
With regard to the User model, I do not have too much experience, but maybe your issues result from the fact that you are not doing it as proposed by Django ?
Just in case that your are intersted, some more interesting posts: h
Extending the User model with custom fields in Django
http://www.b-list.org/weblog/2006/jun/06/django-tips-extending-user-model/,http://www.turnkeylinux.org/blog/django-profile

Related

How to bootstrap style ModelFormset

I've have a view that uses the formset factory but I'm trying to style the rendered html form. But the usual styling for forms doesn't work and im not sure what i need to do to apply the CSS stytles.
My View
def get_questionnaire(request, project_id, questionnaire_id):
# Get the Response object for the parameters
response = Response.objects.get(
project_name_id=project_id, questionnaire_id=questionnaire_id
)
AnswerFormSet = modelformset_factory(Answer, fields=('answer',), extra=0)
answer_queryset = Answer.objects.filter(response=response
).order_by('question__sequence'
).select_related('question')
if request.method == 'POST':
# to be completed
pass
else:
# Get the list of questions for which no Answer exists
new_answers = Question.objects.filter(
questionnaire__response=response
).exclude(
answer__response=response
)
# This is safe to execute every time. If all answers exist, nothing happens
for new_answer in new_answers:
Answer(question=new_answer, response=response).save()
answer_formset = AnswerFormSet(queryset=answer_queryset)
return render(request, 'pages/formset.html', {'formset': answer_formset})
for the moment im just trying to style a single field by applying the widgets in forms.py. But this isn't working.
class answer_formset(ModelForm):
class Meta:
model = Answer
fields = ('answer',)
widgets = {
'answer': forms.Select(attrs={'class': 'form-select'}),
}
HTML
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }} {{ form.instance.question.question }}
{{ form.answer }}
<br>
{% endfor %}
Thanks
You need to tell the factory which ModelForm to use:
AnswerFormSet = modelformset_factory(Answer, form=answer_formset, fields=('answer',), extra=0)
From docs:
You can optionally pass a form argument to use as a starting point for
constructing the ModelForm.
more info: https://docs.djangoproject.com/en/4.0/ref/forms/models/#modelform-factory

How to integrate a form into a Detail View?

I would like to do:
I am trying to create a form input on a detail view that will update a particular data column ('status') of the detailed model instance. Here is a picture of what I have in mind:
The selector would display the current status and the user could change it and update from the detail view without having to access the UpdateView.
my idea here would be to have this happen:
1. On submit, get the new user entered value.
2. get the model instance of the currently detailed class
3. assign the model instance attribute as the user entered value
4. save the model instance
I've tried: I don't know if this is the best way to do this but i've been trying to create an AJAX call, mostly by looking for examples online.
Results: Terminal shows Post on submit: "[19/Nov/2019 17:50:33] "POST /task/edit/4 HTTP/1.1" 200 41256". However, the data is not saved to the db. On refresh, the selector returns to previously saved status.
The console shows: "script is connected", and "Update Status" with no errors. On submit, the alert displays success message: "127.0.0.1:8000 says status updated".
Task_detail.html
<div class="deliv-box edit">
<form id="status-update-form" method='POST' action='{% url "task_edit" task.pk %}'>
{% csrf_token %}
{{task_form.status}}
<input id="status-update-btn" type="submit" value="Update Status" />
</form>
</div>
...
<script type="text/javascript">
var frm = $('#status-update-form');
frm.submit(function () {
console.log("script is connected")
console.log($('#status-update-btn').val())
$.ajax({
type: frm.attr('method'),
url: frm.attr('action'),
data: frm.serialize(),
success: function (data) {
$("#deliv-box edit").html(data);
alert("status updated");
},
error: function(data) {
alert("error");
}
});
return false;
});
</script>
forms.py
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = "__all__"
views.py
class TaskDetail(ModelFormMixin, DetailView):
template_name='task_detail.html'
model = Task
form_class = TaskForm
def get_context_data(self, **kwargs):
context = super(TaskDetail, self).get_context_data(**kwargs)
context['task_form'] = self.get_form
return context
def update(request):
if request.method=='POST':
task_id = request.POST.get('id')
task = Task.objects.get(pk = task_id)
status_obj = request.POST.get('status')
task.status = status_obj
task.save()
return JsonResponse({'status':'updated...'})
else:
return JsonResponse({'status':'not updated'})
thank you.
A solution:
In the unlikely event that someone stumbles across this question and who is, like me, just trying to figure it out all by themselves, here is what I've learned about how this works: When a user wants to update a form, Django pre-populates the form with the existing data related to that instance. A user can then alter the data and re-submit the form.
Here, I was attempting to alter just one field of the exiting instance, but as I was only calling that one field, Django was assuming not, as I had hoped, that the other fields would remain the same, but that I intended the other fields to be submitted as blank. Where the fields are required one cannot return that field as blank. Therefore, Django was not able to validate the form and so the form did not get updated.
A solution that works is to call all the fields as hidden and show just the one you want to alter. This way Django can return the unaltered data and validate the form, and you get an update button on your detail view:
<form method="POST">
{% csrf_token %}
<h4> STATUS: </h4>
{% for field in form %}
{{ field.as_hidden }}
{% endfor %}
{{form.status}}
<button type="submit" class="btn btn-success">submit</button>
</form>
You are overriding the method update which does not exist, so it is never called.
You need to subclass UpdateView instead of the DetailView and the mixin.
class TaskUpdateView(UpdateView):
template_name='task_detail.html'
model = Task
form_class = TaskForm
# you can use the line below instead of defining form_class to generate a model form automatically
# fields = ('status', )
def form_valid(self, form):
post = form.save(commit=False)
# do anything here before you commit the save
post.save()
# or instead of two lines above, just do post = form.save()
return JsonResponse({'status':'updated...'})
Here is how you would add readonly (disabled) fields to your form:
class TaskForm(forms.ModelForm):
# override the default form field definitions for readonly fields
other_field = forms.CharField(disabled=True)
another_field = forms.IntegerField(disabled=True)
class Meta:
model = Task
fields = ("status", "other_field", "another_field")
# you could also just do:
# fields = '__all__'

Enforce form field validation & display error without render?

I'm a django newbie so a verbose answer will be greatly appreciated. I'm enforcing a capacity limit on any newly created Bottle objects in my model, like so:
class Bottle(models.Model):
name = models.CharField(max_length=150, blank=False, default="")
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, related_name="bottles")
vintage = models.IntegerField('vintage', choices=YEAR_CHOICES, default=datetime.datetime.now().year)
capacity = models.IntegerField(default=750,
validators=[MaxValueValidator(2000, message="Must be less than 2000")
,MinValueValidator(50, message="Must be more than 50")])
My BottleForm looks like so:
class BottleForm(ModelForm):
class Meta:
model = Bottle
fields = '__all__'
My view (with form validation logic based on this answer):
def index(request):
args = {}
user = request.user
object = Bottle.objects.filter(brand__business__owner_id=user.id).all(). \
values('brand__name', 'name', 'capacity', 'vintage').annotate(Count('brand')).order_by('brand__count')
args['object'] = object
if request.method == "POST":
form = BottleForm(request.POST)
if form.is_valid():
bottle = form.save(commit=False)
bottle.save()
return redirect('index')
else:
form = BottleForm()
args['form'] = form
return render(request, template_name="index.pug", context=args)
And my template (in pug format), like so:
form(class="form-horizontal")(method="post" action=".")
| {% csrf_token %}
for field in da_form
div(class="form-group")
label(class="col-lg-3 col-md-3 col-sm-3 control-label") {{field.label_tag}}
div(class="col-lg-9 col-md-9 col-sm-9")
| {{ field|add_class:"form-control" }}
input(class="btn btn-primary")(type="submit" value="submit")
After a few hours of messing with my code and browsing SO, I managed to display the error by adding {{ form.errors }} to my template, but that only shows after the page has already been reloaded and in a very ugly form: see here.
What I'd like is to utilize django's built-in popover error messages without reloading page (see example on default non-empty field), which is so much better from a UX standpoint.
That is not a Django message. That is an HTML5 validation message, which is enforced directly by your browser. Django simply outputs the input field as type number with a max attribute:
<input type="number" name="capacity" max="750">
I'm not sure if your (horrible) pug templating thing is getting in the way, or whether it's just that Django doesn't pass on these arguments when you use validators. You may need to redefine the field in the form, specifying the max and min values:
class BottleForm(ModelForm):
capacity = forms.IntegerField(initial=750, max_value=2000, min_value=250)
(Note, doing {{ field.errors }} alongside each field gives a much better display than just doing {{ form.errors }} at the top, anyway.)

Django: Unable to open a detail view by URL, which causes reverse argument errors in another view

Python 3.5.1
Django 1.10
Been tearing my hair out for hours on this, but have my Reverse Argument error pinned down to the actual problem.
When I try to open a form to edit a particular record in my model, it only opens a blank (unconnected) form. Using the same logic, I am able to delete a record, so I'm sure this is something stupid-simple. But I'm too many hours into this, so I would appreciate a lifeline.
From models.py
class CmsSpaces(models.Model):
sid = models.AutoField(db_column='SID', primary_key=True)
section = models.CharField(db_column='Section', max_length=5)
...Many more columns...
def __unicode__(self):
return self.name
def get_absolute_url(self):
return reverse('cms_spaces:space_edit', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_delete', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_new', args = None, kwargs = None)
class Meta:
managed = False
db_table = 'cms_spaces'
From views.py
def CmsSpaces_update(request, sid,
template_name='space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
return render(request, template_name, {'form':space_form})
def CmsSpaces_delete(request, sid,
template_name='space_delete.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
if request.method=='POST':
space.delete()
return redirect('space_list')
return render(request, template_name, {'object':CmsSpaces})
From urls.py
from django.conf import settings
from django.conf.urls import include, url
from django.contrib import admin
from cms_spaces import views
urlpatterns = [
url(r'^space_list/$', views.CmsSpaces_list, name='space_list'),
url(r'^space_new/$', views.CmsSpaces_create, name='space_new'),
url(r'^space_edit/(?P<sid>[\w-]+)/$', views.CmsSpaces_update, name='space_edit'),
url(r'^space_delete/(?P<sid>[\w-]+)/$', views.CmsSpaces_delete, name='space_delete'),
]
From space_edit.html. When I enter the url directly for .../space_delete/12345, it does proceed to delete the record with sid=12345.
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Add or Update Space" />
</form>
From space_list.html (which throws "Reverse for 'space_edit' with arguments '(10256,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []") as an error. 10256 is in fact an sid for one of the records in the table. When I remove the links to space_edit and space_delete, it does render the list of records. Yes, there are many more lines of code that handle presentation, but this is the boiled down version, and it is "broken" even at this level.
<ul>
{% for CmsSpaces in space_list %}
<li>{{CmsSpaces.sid}} {{ CmsSpaces.section }} {{ CmsSpaces.space}} {{ CmsSpaces.lot }} {{ CmsSpaces.deednum}}
edit
delete
</li>
{% endfor %}
</ul>
All I want to do is be able to call and edit a record. If I can do that, I believe the list view will work, and then I'm well on my way to home free. The issue seems to be that even though it is correctly capturing the sid, it is failing to correctly pass it as an argument to the model. Help! Thanks.
EDIT
from django import forms
from django.forms import ModelForm
from masterdata.models import CmsSpaces
class space_form(ModelForm):
class Meta:
model = CmsSpaces
fields = [
'sid',
'section',
'space',
'lot',
'deednum',
'purchdate',
'notes',
'perpetualcare',
]
Okay... first off a big thanks to #knbk for getting me on track with the list issue.
Second, my initial diagnosis in the OP was wrong. The issue was in my CmsSpaces_update view. Boiled down, the view had the pieces to tie the form to the record, but no actual code to do so. slaps head
So because someone else may someday read this and wonder the same thing...
def CmsSpaces_update(request, sid,
template_name='templates/space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
ctx = {}
ctx['form'] = form
ctx['space'] = space
return render(request, template_name, ctx)

Form fields missing in Django, just button visable

New to Django and having problem seeing form fields displayed. What I see is just the submit button. If pressed, the form is finally presented, but with the format for a form that had bad data (typical 'this field is required' error for each box, red box, etc).
The form works fine after entering data and again pressing submit (stores entries in my db). I have a number of forms on the same page that have the same behavior.
Example of one form:
#model
class dbPara(models.Model): #parameters
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
username = models.CharField(max_length=10)
turns = models.FloatField(default=27)
units = models.FloatField(default=5)
rise = models.FloatField(default=2.9)
rescutL = models.FloatField(default=0.0833333333)
rescutH = models.FloatField(default=0.333333333)
LorR = models.CharField(max_length=1, default='R')
def __str__(self):
return self.timestamp, self.username, self.turns, self.units, self.rise, self.rescutL, self.rescutH, self.LorR
#form
class ParaForm(ModelForm):
class Meta:
model = dbPara
widgets = {'username': forms.HiddenInput()}
fields =['username', 'turns', 'units', 'rise', 'rescutL', 'rescutH', 'LorR']
#view
def importParameters(request):
if request.method == 'GET':
form = ParaForm()
else:
form = ParaForm(request.POST)
if form.is_valid():
entry=dbPara(username = request.POST.get('username'),
turns = request.POST.get('turns'),
units = request.POST.get('units'),
rise = request.POST.get('rise'),
rescutL = request.POST.get('rescutL'),
rescutH = request.POST.get('rescutH'),
LorR = request.POST.get('LorR')
)
entry.save()
return render(request, 'main.html',
{'ParaHTML' : form })
#url
urlpatterns = patterns('Inputs.views',
url(r'^importParameters/$', 'importParameters', name='urlParameters'),
)
#main.html
<div class='col-lg-3'>
<h4>Set Rosetta Parameters</h4>
<action="{% url "urlParameters" %}" method="post">{% csrf_token %}
{{ ParaHTML|crispy }}
<input type="hidden" name = "username" value = "{{ user.get_username }}">
<input type="submit" class="btn btn-primary" value="Set">
</form>
</div>
Appreciate any advice (better simple than 'most correct but complicated')
Could it be due to using default in the model? Would that not 'fill in the form' and result in 'POST' at the initial visit to the page, resulting in just the button? Thoughts?
One Suggesestion here ....
if Using request.POST.get('anything') simply then it Will raise error if particular string not find as in example('anything') string...
Because request.POST.get('anything') will return None if 'anything' is not in request.POST.
Additionally, .get allows you to provide an additional parameter of a default value which is returned if the key is not in the dictionary.
e.g: Corrected will be request.POST.get('anything', 'mydefaultvalue')