Django : How to exclude form field if the user is staff? - django

How to exclude form fields if the user is not staff ? I tried this but didn't work , giving an error :
global name 'user' is not defined
class PostForm(ModelForm):
class Meta:
model = Photo
exclude = ['author','featured','published']
def __init__(self, *args, **kwargs):
published = kwargs.pop('published', None)
super(PostForm, self).__init__(*args, **kwargs)
if not user.is_staff:
del self.fields['published']
view.py
def addpost(request):
if request.method == 'POST':
form = PostForm(request.POST,request.FILES,user=request.user)
if form.is_valid():
post = form.save(False)
post.author = request.user
form.save()
return HttpResponseRedirect(reverse('insight.content.views.index', ))
else:
form = PostForm(user=request.user)
ispost = True
return render_to_response('form_add_place.html', {'form': form,'ispost':ispost},context_instance=RequestContext(request))

This can be achieved in the template when rendering the form. It will need to allow null values or have a default value in the model definition or alternatively have its validation overridden:
<form method="post">{% csrf_token %}
{% if request.user.is_staff %}
<p>{{ form.published }}</p>
{% endif %}
<p>{{ form.author }}</p>
<!-- ... your other fields -->
</form>
Similarly you can check for is_superuser or check permissions, see the docs: https://docs.djangoproject.com/en/dev/topics/auth/default/#permissions

You need to pass it the user instance from your request - the model form doesn't have access to it.
my_form = PostForm(user=request.user)
Then, in your __init__:
def __init__(self, *args, **kwargs):
published = kwargs.pop('published', None)
user = kwargs.pop('user', None)
super(PostForm, self).__init__(*args, **kwargs)
if not user.is_staff:
del self.fields['published']

Related

Is it possible to pass a specific value to a django formset?

So I've been playing with Django formsets for about a week and I'm about ready to throw out Django altogether. Just kidding. Kinda. :). I have the standard inline formset implementation working finally as I documented here. How do I properly implement Django formsets with a CreateView ( Class Based View )? Hope it helps someone. Now I'm trying to leverage this code by trying to pass a specific queryset to it as shown below.
class PlayerFormSet(PlayerFormSet,BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(PlayerFormSet, self).__init__(*args, **kwargs)
self.queryset = Player.objects.filter(team_pk=20)
As shown above, if I hard code a value, no worries it works to populate my formset. Although hardcoding this value is of little value to the implementation. I have researched form_kwargs, played around with numerous implementations as shown throughout SO, about passing a value but nothing I try works other than the code above for one team obviously and I can't for the life of me figure out how to do something like:
class PlayerFormSet(PlayerFormSet,BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(PlayerFormSet, self).__init__(*args, **kwargs)
self.queryset = Player.objects.filter(self.object.team_pk)
Or...
class PlayerFormSet(PlayerFormSet,BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(PlayerFormSet, self).__init__(*args, **kwargs)
self.queryset = Player.objects.filter(self.instance.team_pk)
I know the pk, it can clearly be passed here in a hardcoded fashion...And I have the value accessible in my view. I just can't figure out how to pass it to the formset to get the proper queryset. Is this even possible? This seems to be way more challenging than it should be. Or of course it could be me. Thanks in advance for any ideas.
Update for more thorough analysis.
My Models....
class Team(models.Model):
team_name = models.CharField(null=True)
class Player(models.Model):
player_name = models.CharField(null=True)
team_pk = models.IntegerField(null=True)
team = models.ForeignKey("Team",null=True,on_delete=models.CASCADE)
forms.py
PlayerFormSet = inlineformset_factory(Team, Player, extra=0, fields=['player_name',])
class PlayerFormSet(PlayerFormSet,BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(PlayerFormSet, self).__init__(*args, **kwargs)
self.queryset = Player.objects.filter(team_pk=20)
views.py
class UpdateTeamView(LoginRequiredMixin,CreateView):
model = Team
form_class = TeamForm
template_name = 'update_team.html'
def get_context_data(self, **kwargs):
context = super(UpdateTeamView, self).get_context_data(**kwargs)
dropdown = self.request.GET.get("dropdown", None)
queryset = Player.objects.filter(team_pk=dropdown)
player_form = PlayerFormSet(queryset=queryset)
context['player_form'] = player_form
return context
def form_valid(self, form, player_form):
self.object = form.save()
player_form.instance = self.object
player_form.save()
def form_invalid(self, form, player_form):
return self.render_to_response(
self.get_context_data(form=form,
player_form=player_form,
))
def post(self, request, *args, **kwargs):
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Team:team_main_menu'))
else:
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
player_form = PlayerFormSet(self.request.POST)
if (form.is_valid() and player_form.is_valid()):
return self.form_valid(form, player_form)
else:
return self.form_invalid(form, player_form)
My Template...
<form method="POST" enctype="multipart/form-data" id="forms">
{% csrf_token %}
{{ player_form.management_form }}
{{ player_form.non_form_errors }}
{% for hidden in player_form.management_form %}
{{ hidden }}
{% endfor %}
{% for form in player_form.forms %}
{{ form.id }}
<div class="inline {{ player_form.prefix }}">
<div class="leftwidth22">
<div class="width52">
<h2 class="floatright23">Player Name - </h2>
</div>
</div>
<div class="rightwidth53">
<h2 class="width70">
{{ form.player_name }}
</h2>
</div>
{% if player_form.non_form_errors %}
<h3 class="spacer12">
{{ player_form.non_form_errors }}
</h3>
{% endif %}
{% if form.player_name.errors %}
<h3 class="spacer12">
{{ form.player_name.errors }}
</h3>
{% endif %}
{% endfor %}
</form>
If this is the only view using the inlineformset then you can ditch the BaseInlineFormset override and just give the queryset to the formset call in the view.
Because this is an inlineformset it can take an instance of the parent (instead of a queryset of the objects themselves).
def get_context_data(self, **kwargs):
context = super(UpdateTeamView, self).get_context_data(**kwargs)
dropdown = self.request.GET.get("dropdown", None)
instance = Team.objects.get(pk=dropdown)
player_form = PlayerFormSet(instance=instance)
context['player_form'] = player_form
return context
Now the get portion should work. The post and form_valid need fixing too. You don't need to check the form, as all forms will be validated by the formset. Try:
def form_valid(self, player_form):
player_form.save()
return HttpResponseRedirect('/')
def form_invalid(self, player_form):
return self.render_to_response(self.get_context_data(player_form=player_form))
def post(self, request, *args, **kwargs):
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Team:team_main_menu'))
else:
instance = Team.objects.get(team_pk=dropdown)
player_form = PlayerFormSet(self.request.POST,instance=instance)
if player_form.is_valid():
return self.form_valid(player_form)
else:
return self.form_invalid(player_form)

I don't know how to display formset in django

I don't know how to display formset individually.
I understand how to display all at once.
But I tried some model field names, but it didn't work.
#views
class UserEdit(generic.UpdateView):
model = User
form_class = forms.UserUpdateForm
template_name = 'accounts/accounts_edit.html'
success_url = reverse_lazy('accounts:edit')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
#forms
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = profile
fields = ('first_name','last_name','birthday')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
ProfileFormSet = inlineformset_factory(User,profile,form=ProfileUpdateForm,extra=0)
class UserUpdateForm(mixins.ModelFormWithFormSetMixin,forms.ModelForm):
#Userモデルにprofileモデルを入れる
formset_class = ProfileFormSet
class Meta:
model = User
fields = ('username','email',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
#template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.username.label_tag }}
{{ form.username }}
{{ form.email.label_tag }}
{{ form.email }}
{{ form.formset }}
<button type="submit">Submit</button>
</form>
I want to specify and display instead of the method of displaying at once with formset.
Postscript
Mixins are created for data verification and data storage.
I want to display the details of formset display in template.
However, I only know how to display it at once, like {{form.formset}}.
Ideally I want to display it individually like {{form.formset.first_name}}
#mixins
class ModelFormWithFormSetMixin:
def __init__(self, *args, **kwargs):
super(ModelFormWithFormSetMixin, self).__init__(*args, **kwargs)
self.formset = self.formset_class(
instance=self.instance,
data=self.data if self.is_bound else None,
)
def is_valid(self):
return super(ModelFormWithFormSetMixin, self).is_valid() and self.formset.is_valid()
def save(self, commit=True):
saved_instance = super(ModelFormWithFormSetMixin, self).save(commit)
self.formset.save(commit)
return saved_instance
A formset is a set of forms so you have to loop through them {% for subform in form.formset%}{{subform.first_name}}{%endfor%}
Dirkgroten told me in the comments.
Thank you.

POST request done successfully but data not saved in database

I'm trying to save form data in database by POST request, request successfully done but data not saved in database.
models.py
class Image(models.Model):
user = models.ForeignKey(User, related_name='images')
tagName = models.CharField(max_length=255)
instance = models.CharField(max_length=255)
forms.py
class BuildImageForm(forms.ModelForm):
class Meta:
fields = ('user', 'tagName', 'instance')
model = Image
views.py
class BuildImage(LoginRequiredMixin, CreateView):
form_class = BuildImageForm
model = Image
template_name = 'images/buildImage.html'
success_url = 'user/gui'
def get(self, request, *args, **kwargs):
objectlist = request.user.instances.all()
return render(request, 'images/buildImage.html', {'form': forms.BuildImageForm,
'objectlist': objectlist})
def form_valid(self, form):
instance = form.save()
instance.user = self.request.user
instance.tagName = self.request.tagName
instance.instance = str(self.request.instance_name)
instance.save()
return HttpResponse(status=200)
Update - Edited
I have update my view as:
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = BuildImageForm(request.POST)
if form.is_valid():
data = form.cleaned_data
form.instance.user = self.request.user
form.instance.tagName = data['tagName']
form.instance.instance = data['instance']
form.save()
else:
print(form.errors)
return HttpResponse(status=200)
Now it prints that error in console:
<ul class="errorlist"><li>instance<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
[23/Jul/2017 04:36:56] "POST /user/images/buildImage/ HTTP/1.1" 200 0
Try changing your view method like this,
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
return HttpResponse(status=200)
you are actually saving the file before adding data
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.tagName = self.request.tagName
instance.instance = str(self.request.instance_name)
instance.save()
return HttpResponse(status=200)
My issue has been solved just by providing a title attribute in my template for instance input field.
Updated code as:
buildImage.html
<form class="form-horizontal" method="POST" id="dockerForm">
{% csrf_token %}
<fieldset>
{% if not objectlist %}
<h3><strong>Sorry!</strong> You couldn't have created any Instance yet! <strong>Let's Create One </strong></h3>
{% else %}
<div class="form-group">
<span class="col-md-1 col-md-offset-2 text-center"><label for="package">Select Your Instance:</label></span>
<div class="col-md-8">
<select class="form-control" name="instance" id="instance_id" title="Select your Instance">
{% for element in objectlist %}
<option value={{ element.name }}> {{ element.name }} </option>
{% endfor %}
</select>
{% endif %}
</div>
</div>
</fieldset>
</form>
forms.py
class BuildImageForm(forms.ModelForm):
class Meta:
fields = ('tagName', 'instance')
model = Image
views.py
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = BuildImageForm(request.POST)
if form.is_valid():
data = form.cleaned_data
form.instance.user = self.request.user
form.instance.tagName = data['tagName']
form.instance.instance = data['instance']
form.save()
else:
print(form.errors)
return HttpResponseRedirect(reverse('users:gui'))
try to set try and cache for database operation
may be there is some error cached in cache section
Update
try:
"save to database code here"
except Exception as e:
print "error here" print e

django doesn't show ValidationError

I have a problem: when i try to login with information that doesn't get validated, the page is just refreshes, and nothing happens. My goal is to pop up any validation error, but they literally refuse to appear. Could you please check my code, and help me to find out what to do.
template
<form method="post" action="">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% bootstrap_form form %}
<input type="hidden" name="next" value="{{ request.path }}">
<input type="submit" value="Войти">
</form>
form
class LoginForm(forms.ModelForm):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = CustomUser
fields = ('username', 'password')
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username is not None and password:
user = authenticate(username=self.cleaned_data.get('username'), password=self.cleaned_data.get('password'))
if user is None:
raise ValidationError('Неверное имя пользователя или пароль')
if username is None or password is None:
raise ValidationError('Неверные данные')
return self.cleaned_data
view
class LoginView(FormView):
form_class = LoginForm
template_name = 'user/login.html'
def get_success_url(self):
return self.request.META.get('HTTP_REFERER')
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
ctx['form'] = self.form_class
return ctx
def form_valid(self, form):
user = authenticate(username=form.cleaned_data.get('username'),
password=form.cleaned_data.get('password'))
login(self.request, user)
return super().form_valid(form)
def dispatch(self, request, *args, **kwargs):
if self.request.user.is_authenticated():
return redirect('post-list')
return super().dispatch(request, *args, **kwargs)
UPD: It's not bootsrap. When i use the default forms, there are no validationErrors aswell
Your problem is in get_context_data, where you pass the form class, rather than the object instantiated with the post data.
However you should not be overriding that method at all. Even if you fixed this issue, it would only be duplicating what the method already does. Remove the method altogether.

django-crispy-form does not render the submit button and does not render the form action attribute

I am trying to use django-cripsy-for to customize my forms. But it seems that crispy form is somehow not rendering the submit button and the form action attribute.
Here is template i am rendering the form at.(login.html)
{% extends '_base.html' %}
{% block content %}
{{ login_error_message }}
{% load crispy_forms_tags %}
{% crispy form form.helper %}
{% endblock %}
And here is the view
class LoginView(FormView):
form_class = LoginForm
template_name = "login.html"
success_url = reverse_lazy('home')
def form_valid(self, form):
email = form.cleaned_data['email']
password = form.cleaned_data['password']
referrer = self.request.POST.get('referrer')
user = authenticate(email=email, password=password)
if user is not None:
# if user.is_active:
login(self.request, user)
if referrer !="":
self.success_url = referrer
return super(LoginView, self).form_valid(form)
else:
return render(self.request, "login.html", {'form': form, 'login_error_message': "Invalid username and password."})
def get_context_data(self, **kwargs):
result = super(LoginView, self).get_context_data( **kwargs)
param = self.request.GET.get('next', '')
result.update({'param': param})
return result
And here is my form class
class LoginForm(forms.Form):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-form'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
self.helper.form_action = 'login'
email = forms.EmailField(max_length=100)
password = forms.CharField(max_length=100)
What am I missing ? Please help .
Regarding the missing submit button:
You have to add it first!
self.helper.add_input(Submit('submit', 'Submit'))
As can be seen in the 4th listing of the Fundamentals documentation
From the Django Docs (https://docs.djangoproject.com/en/1.8/topics/templates/):
{# this won't be rendered #}
You are outputting your form as a series of Django comments in your template for some reason. Why?