MyModelForm' object has no attribute 'user using django - python

i want to create i simple django image processing .first i have create correct a django auth and multi upload images in my app.
now i want the user can select one self image from a list django form where i create and that image i get for my processing.i create something but not work.
i take that error :
'MyModelForm' object has no attribute 'user'
here the code :
views.py
#login_required(login_url="login/")
def myview(request):
Myf = MyModelForm(request.user,request.POST)
return render(request,'home.html',{'Myf':Myf})
forms.py
class MyModelForm(ModelForm):
def __init__(self, *args, **kwargs):
if 'user' in kwargs:
self.user = kwargs.pop('user')
super(MyModelForm, self).__init__(*args, **kwargs)
choices = [(obj.id, obj.upload.url) for obj in MyModel.objects.filter(user=self.user)]
self.fields['upload'].widget = Select(choices=choices)
class Meta:
model = MyModel
fields = ('upload',)
if i replace Myf = MyForm(request.user,request.POST) with Myform = MyModelForm(user=request.user) then i think list worked like this
image list
but i cant take select image for image processing.
html :
<form class="" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ Myform}}
<input type="submit" name="" value="Submit">
</form>
any idea ?

Your MyForm class does not have user in the __init__ method's signature, so you need to provide it as a keyword argument when instantiating the form:
MyForm(user=request.user,data=request.POST)

Related

How to pass a URL value from a template to a form_valid method inside a Class Based View in Django

I started working with django and I have come across a problem that I am not able to solve. I believe this should be easy but I cannot figure it out.
I am getting an Id from a template which I pass through a URL.
data.html
...
<div class="container">
<form method="GET" action="{% url 'data:next' %}">
<input type="hidden" required name='job_id'>
<button type="submit" class="btn btn-primary">Run Next</button>
{% csrf_token %}
</form>
</div>
...
url.py
app_name = "data"
urlpatterns = [
...
...
path('next/', RunNextView.as_view(), name='next'),
]
This seems to pass the job_id value to the URL as after selecting a checkbox (in this case with job_id pointing at 44) and clicking on the Run Next button I get to a url such as:
http://localhost:8000/data/next/?job_id=44&Some-token
which I think is good. Now I want to pass the number 44 to a class based view which is the problem and I was not able to do it.
As shown below I need the job_id in order to run the run_next_task task.
How do i grab that 44 and pass it to the form_valid method inside the CBV ?
views.py
class RunNextView(FormView):
template_name = 'next.html'
form_class = RunForm
success_url = reverse_lazy('data:list')
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect(reverse_lazy('account:login'))
else:
return super(RunNextView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
cal = form.save(False)
cal.user = self.request.user
cal.save()
cal.send_update()
job_id = self.request.GET.get('job_id') //this way doesn't work
run_next_task.delay(pk=cal.pk, dir=job_id)
return redirect(self.success_url)
def form_invalid(self, form):
for field in form.errors:
form[field].field.widget.attrs['class'] += F" {form.error_css_class}"
return self.render_to_response(self.get_context_data(form=form))
I did countless experiments.
job_id = self.request.GET.get('job_id') inside the form_valid and pretty much every other combination. I tried using session and global variables. I tried using kwargs in many different ways such as job_id=self.kwargs['job_id'].
Non of them worked. There must be something basic and fundamental that I am missing.
Any help is appreciated.
Thank you ;]
If you want to send data with a form, you have to choose POST method not GET. add the "job_id" (or job.id. what ever u passed) to the value of the input as follow:
<form method="POST" action="{% url 'data:next' %}">
<input type="hidden" value="{{job_id}}" required name='job_id'>
<button type="submit" class="btn btn-primary">Run Next</button>
{% csrf_token %}
</form>
then in view, define a post method:
class RunNextView(FormView):
def post(self,request):
# Since name='job_id'
posted_id=request.POST["job_id"]
I haven't tried this on my own but just give it a try ;)
I added a new attribute in __init__
Then use it in form_valid.
class RunNextView(FormView):
template_name = 'next.html'
form_class = RunForm
success_url = reverse_lazy('data:list')
# Add this ↓
def __init__(self, **kwargs):
self.job_id = ""
super().__init__(**kwargs) # <- You don't need put 'self' here. So, remove it.
# Add this as well ↓
def get(self, request, *args, **kwargs):
self.job_id = request.GET.get('job_id')
super().get(request, *args, **kwargs)# <- Remove 'self' here
# some code...
def form_valid(self, form):
# some code...
job_id = self.job_id # <- Change here
# some code...
# some code...
EDIT
I am overriding the __init__ function in this example, and I let the parent function whatever it needs to do by doing super().__init__(**kwargs) after I create a new attribute job_id.
You can access self.job_id anywhere inside the class.

Using url parameter in a class based view to set form field

I am creating a form in my django app. My app has a user, a user can have many transactions and a transaction can have many sales. I'm trying to create a form to add sales to the DB. I'm trying to pass a parameter in the URL (transaction_id) and use it in the Django class-based view CreateView to set the corresponding (foreign key) field in the form. Is it possible to do this and if so how could I apply it.
Class based create view below
class SaleCreateView(CreateView):
model = Sale
fields = ['amount_sold', 'total_price_sold', 'note']
def form_valid(self, form):
URL below
path('sale/new/<int:pk>', SaleCreateView.as_view(), name='sale-create'),
Link below
{% url 'sale-create' transaction.id %}
Sale form below
<div>
<form method="POST">
{% csrf_token %}
<fieldset class ="form-group">
<legend class="border-bottom mb-4">Enter Sale</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class ="btn btn-outline-info" type="submit">Enter</button>
</div>
</form>
</div>
Yes, it is possible.
You will need to use a ModelForm with CreateView. Your SaleCreateView with SaleCreateForm classes should look like this:
class SaleCreateView(CreateView):
model = Transaction
fields = ['amount_sold', 'total_price_sold', 'note']
form_class = SaleCreateForm
def get_initial(self, *args, **kwargs):
initial = super(SaleCreateView, self).get_initial(**kwargs)
return initial
class SaleCreateForm(forms.ModelForm):
class Meta:
model = Sale
exclude = ('transaction',)
def __init__(self, *args, **kwargs):
self.transaction = kwargs.pop('transaction')
super(SaleCreateForm, self).__init__(*args, **kwargs)
Here is a step by step tutorial.

take select request from django from

I have made a select django form with list of images for each user.
How do I take that select request in my views.py ?
I have only managed to create correct that select list but I need to take that select request. But I don't know how.
models.py
class MyModel(models.Model):
user = models.ForeignKey(User, unique=True)
upload = models.ImageField(upload_to='upload')
views.py
#login_required(login_url="login/")
def carlist(request):
Myform = MyModelForm(user=request.user)
return render(request,'about.html',{'Myform':Myform})
select django form :
class MyModelForm(ModelForm):
def __init__(self, *args, **kwargs):
# extract "user" from kwrags (passed upon form init)
if 'user' in kwargs:
self.user = kwargs.pop('user')
super(MyModelForm, self).__init__(*args, **kwargs)
# generate the choices as (display, value).
# Display is the one that'll be shown to user, value is
# the one that'll be sent upon submitting
# (the "value" attribute of <option>)
choices = MyModel.objects.filter(user=self.user).values_list('upload', 'id')
self.fields['upload'].widget = Select(choices=choices)
class Meta:
model = MyModel
fields = ('upload',)
html :
<form class="" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ Myform}}
<input type="submit" name="" value="Submit">
for example Myform now has a list of user images, which is correct but after that i need the selected images from the form.
Can do it that with my code or not?
Yes you can do it with your code. In your view, you can get it as:
class MyView(View):
def post(self, request):
form = MyModelForm(request.POST, request.FILES)
if form.is_valid():
upload_inst = form.cleaned_data['upload']
...
This will give you the selected image from the form.
If you want to have multiple images being selected, you could try the following:
https://pypi.python.org/pypi/django-multiselectfield/
https://pypi.python.org/pypi/django-select-multiple-field/

Django remove inline formset record in UpdateView

I have an UpdateView which contains a form and an InlineFormetSet that is related to the form model (I simplified the code below):
#models.py
class Note(Model):
content = models.TextField()
class Dialog(Model):
message = models.TextField()
note = modes.ForeignKey(Note)
#views.py
class NoteUpdateView(UpdateView):
model = Note
form_class = NoteForm
def get_context_data(self, **kwargs):
context = super(NoteUpdateView ,self).get_context_data(**kwargs)
self.object = self.get_object()
dialogFormset = inlineformset_factory(Note,
Dialog,
fields='__all__',
extra=0)
dialog = dialogFormset(instance=self.object)
context['dialog'] = dialog
return context
def post(self, request, *args, **kwargs):
form = self.get_form(self.get_form_class())
dialog_form = DialogFormset(self.request.POST, instance=Note.objects.get(id=self.kwargs['pk']))
if (form.is_valid() and dialog_form.is_valid()):
return self.form_valid(form, result_form, dialog_form)
else:
return self.form_invalid(form, result_form, dialog_form)
def form_valid(self, form, result_form, dialog_form):
self.object, created = Note.objects.update_or_create(pk=self.kwargs['pk'], defaults=form.cleaned_data)
dialog_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, result_form, dialog_form):
return self.render_to_response(self.get_context_data(form=form,
result_form=result_form,
dialog_form=dialog_form))
The purpose of NoteUpdateView is to render existing Note and Dialog when a GET request is made tonote/11. A user may delete an existing Dialog, which is not handled by the code above.
To handle delete, I can do the following on POST:
1) fetch all of the dialog records related to the requested Note:
dialogs = Note.objects.filter(pk=self.kwargs['pk'])
2) loop through self.request.POST and see if the formsets contained in the submitted data also exist in the dialogs created above.
3) If a record is dialogs but not submitted via POST, then that dialog is considered to be removed by the user. Thus, preform delete operation.
I am sure I can implement these steps. But since I am not very familiar with Django's formset. I wonder if there is any built-in classes or methods that I should use to automate these steps. What is the Django way of doing what I just described?
Ok, I figured out what the problem was. The problem that I run into is due to the use of django-crispy-forms. Let me explain what happened:
When Django renders InlineFormSet, it's can_delete attribute is set to True automatically. When this attribute is set to True, a hidden input field is inserted into the rendered HTML:
<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">
I used django-crispy-forms to render my forms so that they are styled with bootstrap3. When rendering inlineformset using crispy-forms, a FormHelper needs to be defined.
This is because when you have multiple inlineformset forms on the page, you will only want one <form> tag surrounds them instead of giving each inlineformset form it's own <form> tag. To do that, I had to define the FormHelper like this:
#models.py
class Dialog(Model):
info1 = models.TextField()
info2 = models.TextField()
#forms.py
class DialogFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DialogFormSetHelper, self).__init__(*args, **kwargs)
self.form_tag = False # This line removes the '<form>' tag
self.disable_csrf = True # No need to insert the CSRF string with each inlineform
self.layout = Layout(
Field('info1', rows='3'), # make sure the name of the field matches the names defined in the corresponding model
Field('info2', rows='3')
)
I need django-crispy-forms to set the row number of a textarea tag to be 3. Thus, I had to specifically redefine how my textarea fields look like under Layout. What I didn't realize when using the Layout is that anything that you didn't define in it will not be rendered in the HTML.
From the look of the code, I didn't miss any fields defined in the Dialog model. But, what I didn't realize is that the hidden fields that come with InlineFormSet are not rendered in the HTML unless I specifically define them in the Layout object and in the template. To get formset & inlineformset working properly, you will need the following hidden inputs:
formset.manageform. They look like this in the HTML:
<input id="id_dialog_set-TOTAL_FORMS" name="dialog_set-TOTAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-INITIAL_FORMS" name="dialog_set-INITIAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-MIN_NUM_FORMS" name="dialog_set-MIN_NUM_FORMS" type="hidden" value="0">
<input id="id_dialog_set-MAX_NUM_FORMS" name="dialog_set-MAX_NUM_FORMS" type="hidden" value="1000">
The primary key that is associated with each inlineformset form, and a foreign key that the inlineformset refers to. They look like this in HTML:
<input id="id_dialog_set-0-note" name="dialog_set-0-note" type="hidden" value="11"> <!-- This line identifies the foreign key`s id -->
<input id="id_dialog_set-0-id" name="dialog_set-0-id" type="hidden" value="4"> <!-- This line identifies the inlineformset form`s id -->
[A DELETE hidden field when can_delete is set to True] (https://docs.djangoproject.com/en/1.9/topics/forms/formsets/#can-delete). It looks like this in the HTML:
<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">
In my template, I had the first two covered:
<form method="post" action="{{ action }}" enctype="multipart/form-data" id="note_form">
{% crispy form %}
{# the management_form is covered here #}
{{ dialog.management_form }}
{% for form in dialog %}
<div class="formset-container">
<div class="dialog-title">
{% crispy form dialogHelper %}
</div>
{# the hidden fields are covered here #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
</div>
{% endfor %}
</form>
What I didn't have is the DELETE hidden input. To add it to the HTML, I had to add it this way in the Layout:
#forms.py
class DialogFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DialogFormSetHelper, self).__init__(*args, **kwargs)
self.form_tag = False
self.disable_csrf = True
self.layout = Layout(
Field('info1', rows='3'),
Field('info2', rows='3'),
Field('DELETE') # <- ADD THIS LINE
)
Finally, everything works properly now
The Django way is to check if someone has made a library that handles this for you :-).
So take a look at the exellent django-extra-views and it's InlineFormSetView. I've used it a lot and it works really well. In your case your view becomes something like this:
from extra_views import InlineFormSetView
class NoteUpdateView(InlineFormSetView):
model = Note
inline_model = Dialog
form_class = NoteForm
extra = 0
def get_context_data(self, **kwargs):
context = super(NoteUpdateView ,self).get_context_data(**kwargs)
context['dialog'] = context['formset']
return context
You could skip .get_context_data method as well if you update your template to refer to the formset as "formset" instead.

Remove Checkbox for the django image field

I have a model like below
class Book(models.Model):
name = models.CharField(max_length=56)
picture = ImageField(upload_to='/max/')
So when editing the Book model from templates like below
<form enctype="multipart/form-data" action="{% url 'edit_book' book_id %}" method="post">
{% csrf_token %}
{{book_form.name}}
{{book_form.picture}}
</form>
if the book record already has an image an extra html checkbox has been
Currently:
media/product138ba6ccf0d1408d968577fa7648e0ea/assets/bubble.png
<input id="picture-clear_id" name="picture-clear" type="checkbox" /> <label for="picture-clear_id">Clear</label><br />Change:
<input id="selectedFile" name="picture" type="file" />
So if the book has an image already when it has been created, it also has some checkbox and label before it so how to avoid that checkbox ?
Edit
forms.py
class BookForm(ModelForm):
class Meta:
model = Book
def __init__(self, *args, **kwargs):
super(BookForm, self).__init__(*args, **kwargs)
self.fields['picture'].widget.attrs = {'id':'selectedFile'}
I'm a little surprised to be honest, because what you describe looks like ClearableFileInput widget, while according to the documentation, it is FileInput that is used as a default widget.
Still. Try explicitly choosing FileInput:
from django.forms import ModelForm, FileInput
class BookForm(ModelForm):
class Meta:
model = Book
widgets = {
'picture': FileInput(),
}
def __init__(self, *args, **kwargs):
super(BookForm, self).__init__(*args, **kwargs)
self.fields['picture'].widget.attrs = {'id':'selectedFile'}
Update: I'm not surprised anymore. I investigated the issue, and it turned out there simply was a mistake in Django Docs, now corrected. ClearableFileInput is the default widget, so you need to set FileInput explicitly, as shown above.

Categories