django - prevent form submission if submitted form value is X - python

I am using Django 1.4.6 & python 2.7.
I have a test form that allows the user to enter data from a select list:
<select name="language_code" id="id_language_code">
<option value="en-CA">English (Canada) - English (Canada)‎</option>
<option value="en-GB" selected="selected">English (UK) - English (UK)‎</option>
<option value="en">English (US)</option>
<option value="fr-CA">French (Canada) - français (Canada)‎</option>
<option value="fr">French (France) - français (France)‎</option>
</select>
The form submission is working OK.
However, I need to make a change so that if the user selects the select list option of fr-CA and then submits the form, then I would like to NOT add the record and also redirect the user to a different form - return redirect(settings.MENU_DETAIL_LINK_NAME_DETAILS).
Essentially, if the boolean field language_code_disabled of LanguageVersion model is true, then the user should not be able to add the language version.
I am not exactly sure how to do this. I have been going around in circles on this so I have confused myself on how to achieve this.
Here is my models code:
class NameDetails(models.Model, FillableModelWithLanguageVersion):
user = models.ForeignKey(User)
language_version = models.ForeignKey('LanguageVersion')
name_details_prefix_title = models.CharField(null=True, blank=True, max_length=25)
name_details_first_name = models.CharField(null=False, blank=False, max_length=50)
name_details_middle_name = models.CharField(null=True, blank=True, max_length=100)
name_details_last_name = models.CharField(null=False, blank=False, max_length=60)
name_details_suffix_title = models.CharField(null=True, blank=True, max_length=25)
class LanguageVersion(models.Model):
"""Language version selection for a user"""
user = models.ForeignKey(User)
language_code = models.CharField(max_length=32)
language_code_disabled = models.BooleanField(default=False)
Here is my views.py
#login_required
def name_details_add(request):
language_versions = LanguageVersion.objects.filter(user=request.user)
available_languages = get_available_language_details(language_versions, request.user.userprofile.language_preference)
name_details_num = request.user.namedetails_set.count()
preview_labels = get_name_details_labels(available_languages)
if name_details_num >= settings.MAX_NAME_DETAILS:
return redirect(settings.MENU_DETAIL_LINK_NAME_DETAILS)
if request.method == 'GET':
form = NameDetailsForm(
available_languages=available_languages,
language_preference=request.user.userprofile.language_preference,)
elif request.method == 'POST':
form = NameDetailsForm(
available_languages=available_languages,
language_preference=request.user.userprofile.language_preference,
data=request.POST)
if form.is_valid() and name_details_num < settings.MAX_NAME_DETAILS:
name_detail = NameDetails(user=request.user)
name_detail.fill(form.cleaned_data)
name_detail.save()
messages.success(request, _('successfully added.'))
return redirect(settings.MENU_DETAIL_LINK_NAME_DETAILS)
I am hoping that somone can help me out on this issue.
I need to find out how to call a list of all language codes from the LanguageVersions model of the user where the language_code_disabled is True.
Do I write a function that will loop through the language versions or can I write a simple call function to return a list of disabled language codes, such as en, fr, fr-CA, de ?
disabled_language_versions = LanguageVersion.objects.filter(user=request.user, language_code_disabled=True)

you can add a field validation for the LanguageVersion fk on the form, and access that information in the view:
class NameDetailsForm(forms.ModelForm):
...
def clean_language_version(self):
lang = self.cleaned_data.get('language_version')
if LanguageVersion.objects.filter(language_code=lang, language_code_disabled=True).exists():
raise forms.ValidationError("Language code disabled")
return lang
then in the view:
if form.is_valid() ... :
...
elif form['language_version'].errors:
return redirect(settings.MENU_DETAIL_LINK_NAME_DETAILS)

Well, you can do this:
if request.POST['language_code'] == 'fr-CA':
return HttpResponseRedirect('/some/other/page/')

Related

Django is not populating correctly an specific form using a Queryset

I have created two models Leads and Deals, and I have coded some logic such that if you click a button the Lead becomes a Deal, so what I want it is that a new form is presented to the user but that form already contains the information from the Leads model.
#login_required
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
print(obj.expected_revenue)
form = NewDealForm(request.POST or None, instance=obj)
return render(request,
"account/close_lead.html",
{'form':form})
I have done some debug and printed to the console the queryset and the information is fine, so the queryset is no the problem, the problem is that the NewForm doesn't prepopulate the new values.
models.py (only 2 models shown)
class Leads(models.Model):
CHOICES = (
('Illumination Studies','Illumination Studies'),
('Training','Training'),
('Survey Design','Survey Design'),
('Software License','Software License')
)
STATUS = (('Open','Open'),
('Closed','Closed'),
('Canceled', 'Canceled')
)
project_id = models.BigAutoField(primary_key=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
created_at = models.DateTimeField(auto_now_add=True)
point_of_contact = models.ForeignKey(Client, on_delete=models.CASCADE)
expected_revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
expected_licenses = models.IntegerField(blank=True)
country = CountryField(blank_label='(select country)')
status = models.CharField(max_length=10,choices=STATUS)
estimated_closing_date = models.DateField(blank=True)
services = models.CharField(max_length=20,choices=CHOICES)
def __str__(self):
return f'{self.company}'
class Deal(models.Model):
project_id = models.ForeignKey(Leads, on_delete=models.CASCADE, default='id')
agent = models.ForeignKey(Profile, on_delete=models.CASCADE, default="agent")
service = models.ForeignKey(Leads, on_delete=models.CASCADE, related_name='service')
closing_date = models.DateField(auto_now_add=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE,default='client')
licenses = models.ForeignKey(Leads,on_delete=models.CASCADE, related_name='licenses')
revenue = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
comments = models.TextField(blank=True,null=True)
Now, it could be that I have to inherit from a different form?
forms.py (only NewDealForm)
class NewDealForm(forms.ModelForm):
class Meta:
model = Deal
fields = ['agent','client','project_id','service', 'licenses','revenue', 'comments']
Obviously, worst-case scenario is to create a dictionary to extract the data from the queryset and then pass it to the form, but I'm sure Django has a more elegant way to handle this process.
Well, I guess sometimes Stack Overflow pushes you to solve your own issues, this is the solution.
Essentially, the initial=queryset value was not initializing the form mainly because I have very specific relationships in my model, so what I did is to create a dictionary (key:value) with the form field as key, and my queryset from my model as the value, the code is as below:
'''
def close_lead(request):
if request.method == 'POST':
deal_form = DealForm(request.POST)
if deal_form.is_valid():
deal_form.save()
messages.success(request, 'You have successfully updated the status from open to Close')
id = request.GET.get('project_id', '')
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
return HttpResponseRedirect(reverse('dashboard'))
else:
messages.error(request, 'Error updating your Form')
else:
id = request.GET.get('project_id', '')
obj = get_object_or_404(Leads, project_id=id)
m = obj.__dict__
keys = Leads.objects.get(project_id=m['project_id'])
form_dict = {'project_id':keys.project_id,
'agent':keys.agent,
'client':keys.point_of_contact,
'company':keys.company,
'service':keys.services
}
form = NewDealForm(request.POST or None,initial = form_dict)
return render(request,
"account/close_lead.html",
{'form':form})
'''
As you can see, I create an object dictionary because the forms are different, so they share some common values not all, and then I simply adapt the dictionary, nice and easy, but I somehow expected that Django somehow finds relationships by name?, but maybe the batteries are not included for this.

Direct assignment to the forward side of a many-to-many set is prohibited. Use Class.set() instead

When I try to save Class which is in many to many relationship django throws the following error
TypeError at /class-create/
Direct assignment to the forward side of a many-to-many set is prohibited. Use Class.set() instead.
My views.py looks like this
#login_required()
def create_class(request):
tea_user = request.user.username
validate = teacher_validation(tea_user)
if validate:
if request.method == 'POST':
Link = request.POST.get('link')
Subject = request.POST.get('Subject')
Class = request.POST.get('Class')
teacher_user = Teacher.objects.get(User=request.user)
teacher = Teacher.objects.get(id=teacher_user.id)
created_class = Online_Class(Link=Link, Subject=Subject, Created_by =teacher, Class=Class)
created_class.save()
return render(request, 'online_class/Teacher/class-create.html')
else:
messages.warning(request, 'Sorry You Dont have Permission to access this page')
return redirect('logout')
And my models.py file looks like this
class Online_Class(models.Model):
Created_by = models.ForeignKey(Teacher, on_delete=models.DO_NOTHING)
Class = models.ManyToManyField(Classes)
Subject = models.CharField(max_length=100)
Link = models.CharField(max_length=200)
Joined_by = models.ManyToManyField(Student, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
choice = (('Yes','Yes'),('No', 'No'))
Class_Ended = models.CharField(choices=choice, default='No', max_length=10)
Please help me figure it out
You can not set class=Class in:
created_class = Online_Class(Link=Link, Subject=Subject, Created_by =teacher, Class=Class)
since Class is a ManyToManyField, and thus can not be set like that, you first create the OnelineClass, and then add an entry (or more entries) with created_class.Class.set(&hellip):
#login_required
def create_class(request):
tea_user = request.user.username
validate = teacher_validation(tea_user)
if validate:
if request.method == 'POST':
Link = request.POST.get('link')
Subject = request.POST.get('Subject')
Class = request.POST.get('Class')
teacher_user = Teacher.objects.get(User=request.user)
teacher = Teacher.objects.get(id=teacher_user.id)
created_class = Online_Class.objects.create(
Link=Link,
Subject=Subject,
Created_by =teacher
)
created_class.Class.set([Class])
return render(request, 'online_class/Teacher/class-create.html')
else:
messages.warning(request, 'Sorry You Dont have Permission to access this page')
return redirect('logout')
Note: normally a Django models, just like all classes in Python are given a name in PascalCase, not snake_case, so it should be: OnlineClass instead of Online_Class.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: class instead of Class.
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

Django edit form won't validate on form.save() with user field

I recently added a "user" field to my Game model. I can create a new game that works fine; it's when I want to allow a user to edit the instance of a game where I am running into problems. My view is calling the form = GameForm(request.POST, instance=game) where game = Game.objects.get(pk=id). The form is pre-populated with the correct data, but when it's submitted, whether there are updates or not, the form is not validating. It sees it as a POST, but cannot get inside the if form.is_valid() conditional. And this is ever since I added the user field. I am using the default Django User model, and the field name is called "owner." It is set up as a ManyToManyField(User, blank=True) as users can own many games, and games can be owned by many users. Django forms the Many-To-Many "through" table, but I don't want the user to be able to change who owns what. I have it as a hidden field in my forms.py so a user can't change it.
Model
class Game(models.Model):
game_title = models.CharField(max_length=100,
verbose_name='Game Title',
db_column='game',
blank=False,
null=False,
unique=True)
game_developer = models.CharField(max_length=100,
verbose_name='Developer',
db_column='developer',
blank=True,
null=True)
game_release = models.DateField(max_length=50,
verbose_name='Release Date',
db_column='release_date',
blank=False,
null=True)
rating = models.IntegerField(verbose_name='Game Rating',
db_column='rating',
choices=INT_CHOICES,
blank=True,
null=True)
game_genre = models.CharField(max_length=100,
verbose_name='Genre',
db_column='genre',
blank=False,
null=True,
choices=GENRE_CHOICES)
game_platform = models.CharField(max_length=100,
verbose_name='Game Platform',
db_column='platform',
blank=True,
choices=PLATFORM_CHOICES)
game_esrb = models.CharField(max_length=100,
verbose_name='ESRB Rating',
db_column='esrb',
blank=False,
null=True)
owner = models.ManyToManyField(User, blank=True)
objects = models.Manager()
def __str__(self):
return self.game_title
class Meta:
db_table = 'tbl_games'
verbose_name = 'Game'
View
# Allows the user to update game information
def editGame(request, id):
# Finds the user selected game by game id
game = Game.objects.get(pk=id)
user = request.user.id
if request.method == 'POST':
print("Seen as POST")
# Create game instance pre-populated into a form
form = GameForm(request.POST, instance=game)
if form.is_valid():
print("Form is valid!")
# Saves the edits without saving to the dB
form.save()
messages.success(request, 'Game successfully updated!')
return redirect('library')
else:
print('Seen as GET')
form = GameForm(instance=game)
print("Page loaded")
context = {'form': form}
return render(request, 'library/editGame.html', context)
Form
class GameForm(ModelForm):
game_esrb = forms.CharField(required=False, widget=HiddenInput)
owner = forms.HiddenInput()
def __init__(self, *args, **kwargs):
super(GameForm, self).__init__(*args, **kwargs)
class Meta:
model = Game
fields = [
'game_title', 'game_developer', 'rating', 'game_release',
'game_genre', 'game_platform', 'game_esrb', 'owner'
]
widgets = {
'rating': Select(attrs={'choices': INT_CHOICES}),
'game_genre': Select(attrs={'choices': GENRE_CHOICES}),
'game_platform': Select(attrs={'choices': PLATFORM_CHOICES}),
'esrb_rating': HiddenInput(),
'owner': HiddenInput
}
help_texts = {
'rating':
'Key: 1 - Bad | 2 - Okay | 3 - Average | 4 - Good | 5 - Great'
}
Template
{% block appcontent %}
<div class="height">
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<div class="form-btn">
<a class=" btn btn-secondary cancel" type="button" href="{% url 'library' %}">Cancel</a>
<button class="update btn btn-primary" type="submit">Update</button>
</div>
</form>
</div>
{% endblock %}
First, remove 'owner' from the list assigned to fields in your GameForm class:
class Meta:
model = Game
fields = [
'game_title', 'game_developer', 'rating', 'game_release',
'game_genre', 'game_platform', 'game_esrb', # 'owner' removed
]
Here's why: inside your ModelForm, you only need to name the Game fields that you want to use as input fields in the form. In your case, you don't actually want the 'owner' field to be an input field, so just leave it out of this list. This will accomplish your stated goal of disallowing a user from modifying the owner field.
Now let's look at, and modify, your view:
You do this in your view: "user = request.user.id" This will assign an int to your attribute 'user'. I'm guessing you wanted a user object, not an int, but it's not clear, since you never actually use this attribute anywhere in your view.
Here's what needs to happen: Your view needs to connect the user object to the game object, then save the game object. We've already made sure that 'owner' doesn't appear as an input field in your form. Now, we need to manually provide a value for that field, then save the object. Below, we do this following the is_valid() call in your view:
def editGame(request, id):
# Finds the user selected game by game id
game = Game.objects.get(pk=id)
if request.method == 'POST':
print("Seen as POST")
# Create game instance pre-populated into a form
form = GameForm(request.POST, instance=game)
if form.is_valid():
print("Form is valid!")
form.save()
game.owner.add(request.user) # update the game's owner field, assigning the current user
game.save()
messages.success(request, 'Game successfully updated!')
return redirect('library')
To summarize and clarify: The end result of all this work is an updated Game object. The user updates values via the ModelForm. The view validates those changes, then saves the form, which saves the corresponding game object. Then, we manually assign the user to the 'owner' field of the game, because we intentionally left that field out of the form. Then we save the game again so this change is included in the 'final' state of the object.

How can I populate a manytomanyfield , using view, without django pre-built forms?

I'm building a storage web system using django, I'm very newbie on the framework, so the problem is that, there is a business rule, which demands, two kinds of products, the inside products, and the finished ones. And the finished ones, always are composed by one or more inside products, I have the idea of using the manytomanyfields, but now, I don't really know how to extract this data , that should be a multiple choice, from the form and save in the database, does anyone has any tips or better ideas?
Models.py
class Produto(models.Model):
codigo = models.CharField(max_length=254, null=True)
produto_desc = models.CharField(max_length=200, null=False)
tipo = models.CharField(max_length=2)
qtd = models.IntegerField(null=True, default=0)
created = models.DateTimeField(default=timezone.now, editable=False)
last_updated = models.DateTimeField(default=timezone.now, editable=False)
#Relationship Fields
estrutura = models.ManyToManyField(
'storage.Produto',
related_name="produto"
)
def __str__(self):
return self.produto_desc
Views.py
def CadastroProd(request):
temp = 0
lista_produto = Produto.objects.order_by('id')[:20]
for i in lista_produto:
temp += 1
if request.method == 'POST':
form = NovoProduto(request.POST)
if form.is_valid():
obj = Produto()
obj.save(commit=False)
obj.codigo = form.cleaned_data['codigo']
obj.produto_desc = form.cleaned_data['produto_desc']
obj.tipo = form.cleaned_data['tipo']
# obj.estrutura = form.cleaned_data['estrutura']
obj.save()
return HttpResponseRedirect('/storage/produtos')
lista_produto = Produto.objects.order_by('id')[:20]
lista_pi = Produto.objects.filter(tipo='PI')
lista_pa = Produto.objects.filter(tipo='PA')
context = {'lista_produto': lista_produto,
'temp': temp,
'lista_pi': lista_pi, 'lista_pa': lista_pa,
}
return render(request, 'storage/cadproduto/cadproduto.html', context)
forms.py
class NovoProduto(forms.Form):
codigo = forms.CharField(label='codigo', max_length=254)
produto_desc = forms.CharField(label='produto_desc', max_length=100)
tipo = forms.CharField(label='tipo', max_length=2)
estrutura = forms.IntegerField()
index
<div class="row">
<div class="col-md-3 mb-3">
<label for="pi-ida">Composição de Produtos Internos</label>
<select name="estrutura" multiple id="id_estrutura" required>
{%for prod in lista_pi%}
<option value="{{prod.id}}">{{prod.produto_desc}}</option>
{% endfor %}
</select>
</div>
</div>
<hr class="mb-4">
<button class="btn btn-primary btn-lg btn-block" type="submit">Cadastrar</button>
I expected that I can get information of the product, adn of the products that compose it
You are using your own custom template so get the selected choices list using the getlist() method and use set() method to save the manytomany field like this
Also if form is valid you need to save the form
if request.method == 'POST':
form = NovoProduto(request.POST)
estrutura = request.POST.getlist('estrutura')
if form.is_valid():
obj=form.save(commit=False)
obj.codigo = form.cleaned_data['codigo']
obj.produto_desc = form.cleaned_data['produto_desc']
obj.tipo = form.cleaned_data['tipo']
# obj.estrutura = form.cleaned_data['estrutura']
obj.save()
obj.estrutura.set(estrutura)
return redirect....
In your Form subclass, use ModelMultipleChoiceField:
class NovoProduto(forms.Form):
codigo = forms.CharField(label='codigo', max_length=254)
produto_desc = forms.CharField(label='produto_desc', max_length=100)
tipo = forms.CharField(label='tipo', max_length=2)
estrutura = forms.ModelMultipleChoiceField(queryset=Produto.objects.order_by('id')[:20])
The M2M field seems to be a self reference (unless you have two Models called Produto), so you might want to exclude the current product from the list of select options.
You can modify the queryset of a ModelMultipleChoiceField in the form's constructor:
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance and self.instance.id:
self.fields['estrutura'].queryset = Produto.objects.exclude(id=self.instance.id).order_by('id')[:20]

Poll application with indefinite options, prevent voting from off the options displayed

I'm working on a django voting system where you get displayed a small random amount of options from a pool of candidates, and the way i submit the votes is by using the ID's of the options, but i can't think of a way to keep people from just changing the source code and submitting votes on the same option over and over, the options get displayed on the view and template like this:
return render(request, '/vote.html', {'p': po, 'opts':
opts.order_by('?')[:3]
{%for v in opts%}
<div class='votebox' name='{{v.id}}' onclick='vote()'></div>
{%endfor%}
What's a code efficient way to check if the user voted on an option that was displayed to them?
the voting option model is this
class Submission(models.Model):
def __str__(self):
return self.title
title = models.CharField(max_length=20)
signature = models.CharField(max_length=20)
sub_date = models.DateField()
poll = models.ForeignKey('Poll', on_delete=models.CASCADE)
vote_count = models.IntegerField(null=True)
img = models.ImageField(upload_to='MEDIA_ROOT')
repcount = models.IntegerField()
the Poll model is:
class Poll(models.Model):
title = models.CharField(max_length=20)
author = models.ForeignKey(User, on_delete=models.CASCADE)
start_date = models.DateField()
end_date=models.DateField()
and the view where it goes is this
def poll(request, id):
if request.method == "GET":
po = poll.objects.get(id=id)
daysleft = poll.end_date - datetime.date.today()
opts = po.submission_set.all()
return render(request, '/vote.html', {'p': po, 'opts' : opts, 'daysl':daysleft}
elif request.method == "POST":
voted = request.POST.get('voteval', False)
sub = Submission.objects.get(id=voted)
sub.vote_count += 1
sub.save()
I would add an intermediary model between User and Submission
class Vote(models.Model):
user = models.ForeignKey(User)
submission = models.ForeignKey(Submission)
class Meta:
unique_together = (
('user', 'submission'),
)
Now when handling the POST part of the view, you can check if the user has voted on this submission before and create a new Vote object if he hasn't
elif request.method == "POST":
voted = request.POST.get('voteval', False)
sub = Submission.objects.get(id=voted)
user = request.user
try:
vote = Vote.objects.get(user=user, submission=sub)
# warn user that he's voted for this submission
except Vote.DoesNotExist:
vote = Vote.objects.create(user=user, submission=sub)
sub.vote_count += 1
sub.save()

Categories