i have a very simple model... something like this:
class MachineTransTable(models.Model):
...
file = models.ForeignKey('File', related_name='the_file')
source = models.TextField()
target = models.TextField()
...
What I'd like to do is to have a page where the user has the source on the left (disable), the target on the right (editable) and a submit button to post the edited target text for EACH selected object in the MachineTransTable table. Here are some more information to better understand my request:
A page refers to a single file and there are several (sometimes hundreds) of objects in the MachineTransTable table belonging to the same file
Each time the user edit a single target and hit the submit button for that object, the object is saved/updated (depending on the initial value of the object) in the DB and the the user can continue to edit all the other objects...
At the end of the page there is another submit button which is used to exit from the page at the end of the work (all the objects have been edited/updated). If an object has not been edited/updated, it keeps its original value.
I tried to use a formset but I guess it's not the right choice... This is the file forms.py
class SegmentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SegmentForm, self).__init__(*args, **kwargs)
if self.instance.id:
self.fields['source'].widget.attrs['readonly'] = True
class Meta:
model = MachineTransTable
fields = ['source','target',]
SegmentFormSet = inlineformset_factory(File, MachineTransTable, form=SegmentForm, fields=('source','target'), extra=0)
and the view.py file is:
class CatUpdateView(LoginRequiredMixin,UpdateView):
Model = MachineTransTable
context_object_name = 'file'
template_name = 'app_cat/cat.html'
form_class = SegmentForm
...
def get_context_data(self, **kwargs):
context = super(CatUpdateView, self).get_context_data(**kwargs)
formset = SegmentFormSet(instance=self.get_object())
context['formset_Segment'] = formset
return context
Using this approach, I have a single form and all the related objects are saved/updated at once when the submit button is hit...
What can I do to achieve what I described above? Thanks
I think your choice is correct, you should not use several (hundreds eventually) forms here if the field in the same model. For two reasons:
You have to do a lot of repeat job to write so many forms and that is easy to make mistake and hard to maintain.
You still have to connect the database and update the record no matter how many field had been edit, and they almost efficiency.
But if you really want to do this,you can just use Ajax to post the current parameters name to the api, then update it, For instance, you have a button for target field:
value_in_the_html
Use Ajax to post the field name and value:
$ajax({
url: "api/table_id",
type: "POST",
datatype: "json",
data: {"field": "target", "value": "value_in_the_html"}
});
In view.py:
def UpdateTable(request, id):
field = request.POST.get("field", None)
value = request.POST.get("value", None)
machine = MachineTransTable.objects.filter(id=id).first()
machine.getattr(machine,field) = value
machine.save()
return HttpResponse("Saved!")
Related
So, i have a rather usual "update item" page that is a class-based view which inherits UpdateView. (in views.py it looks like "class ItemUpdateView(UpdateView) and it has method get_success_url(self) defined which contains the redirect url where user will be taken after clicking "Update" button.
My problem is that in my application, there are two different pages that could lead me to this "Update item" page, and depending on the page that user comes from - i want to take the user back to either pageA or pageB upon the successful update of the item.
I wasn't able to find the best-practices of how to handle this anywhere on the web, so - would really appreciate the help.
My guess is that I need to create an additional parameter that will be a part of the url and will contain A or B depending on the pageA or pageB that user came from, i.e. the url itself would be something like '/itemUpdate/int:pk/sourcepage' => '/itemUpdate/45/A'. Does that sound like a correct aproach or is there a better way?
There is a better way that you can check Meta dictionary in request:
write in your views file:
class ItemUpdateView(UpdateView):
previous_url = ''
form_class = UpdateItem
def get(self, request, *args, **kwargs):
self.previous_url = request.META.get('HTTP_REFERER')
print(self.previous_url)
return super().get(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial['success_url'] = self.previous_url
return initial
def form_valid(self, form):
self.success_url = form.cleaned_data['success_url']
print(self.success_url)
return super().form_valid(form)
# also you can use get_success_url instead of form_valid()
# def get_success_url(self):
# return super().get_form().cleaned_data['success_url']
and then write a hidden field in your form and name it success_url
class UpdateItem(forms.ModelForm):
success_url = forms.URLField(widget=forms.HiddenInput)
class Meta:
model=Item
fields=['itemName','quantity']
Note you can not use instance in order to get success_url field, because this field belong to form nor your model instance !
refer to documentions
In my Django form, I have one select field that needs to be populated dynamically based on some information about the current user. I am able to get the field set up correctly and rendered in the form - however, I'm getting an error when submitting the form because it's getting hung up on some of the logic that I have in the form's __init__ method that only makes sense in the context of generating the form in the first place. I'm super-new to Django, and I'm not quite familiar with the design principles for this sort of situation.
In my app's admin.py, I have a method that's used for creating a custom view for a data export form - the relevant parts of it are set up like so...
# admin.py
from organizations.models import Organization
from .forms import ExportForm
class SomeModelAdmin(SimpleHistoryAdmin, SoftDeletionModelAdmin):
def export_view(self, request):
authorized_orgs_queryset = Organization.objects.viewable_for_user(request.user).all()
authorized_orgs = [{'id': org.id, 'name': org.name} for org in authorized_orgs_queryset]
context = dict(
self.admin_site.each_context(request),
form = ExportForm({'authorized_orgs': authorized_orgs}),
)
if request.method == 'POST':
form = ExportForm(request.POST)
if form.is_valid():
# do some stuff with the form.cleaned_data and return a .csv file as a response
return response
return TemplateResponse(request, 'export.html', context)
So the current user may be authorized to export data for multiple organizations, and in the form I'd like to present the user with a select element populated with these organizations.
The ExportForm has a number of "fixed" fields that are always the same, and just the one dynamic select element, which is populated by the authorized_orgs arg that I pass to it - it's defined as...
# forms.py
from django import forms
min_year = 1950
export_formats = [
'csv',
'xls',
'xlsx',
'ods',
'json',
]
class ExportForm(forms.Form):
current_year = datetime.datetime.now().year
export_format = forms.ChoiceField(required=True, label='Format', choices=export_format_choices)
apply_date_range = forms.BooleanField(required=False)
year_from = forms.IntegerField(required=False, disabled=True, min_value=min_year, max_value=current_year, initial=current_year)
year_through = forms.IntegerField(required=False, disabled=True, min_value=min_year, max_value=current_year, initial=current_year)
def __init__(self, *args, **kwargs):
super(ExportForm, self).__init__(*args, **kwargs)
authorized_orgs_choices = [(org['id'], org['name']) for org in args[0]['authorized_orgs']]
self.fields['authorized_org'] = forms.ChoiceField(required=False, label='Choose an authorized organization', choices=authorized_orgs_choices)
When I render the form, all is well. However, form submission is where things go awry. Submitting the form produces the error
File "/code/observations/forms.py", line 28, in __init__
authorized_orgs_choices = [(org['id'], org['name']) for org in args[0]['authorized_orgs']]
File "/usr/local/lib/python3.7/site-packages/django/utils/datastructures.py", line 78, in __getitem__
raise MultiValueDictKeyError(key)
django.utils.datastructures.MultiValueDictKeyError: 'authorized_orgs'
Now, I do understand why this is happening - the __init__ is getting the values from the submitted form as its args, which are different from what I've supplied when setting up the form in the first place.
What I don't know is how this sort of thing should typically be handled in Django... how do I make it so that this dynamic field is created correctly when defining the form to be rendered, and that the data is available to me in form.cleaned_data when it's submitted?
Thanks very much for any insight and help.
Aha - found an answer here - django forms post request raising an error on __init__ method
I needed to make sure to pass those values again when handling the form's POST - I also reworked it a bit to make use of kwargs -
# admin.py
if request.method == 'POST':
form = ExportForm(request.POST, authorized_orgs=authorized_orgs)
# forms.py
def __init__(self, *args, **kwargs):
super(ExportForm, self).__init__(*args)
authorized_orgs_choices = [(org['id'], org['name']) for org in kwargs['authorized_orgs']]
self.fields['authorized_org'] = forms.ChoiceField(required=False, label='Choose an authorized organization', choices=authorized_orgs_choices)
I am new to Django and do not know what I need to know to do this task. I am tasked with creating a web app that has two models. Model A is the employee and model B is a company that contains many employees. During signup, I have a form for model A. Once model A form is filled out, I need to pass an id from the employee to the company signup url so that when I save the company model to the table, I can make sure that the employee id is stored and so the two tables are related. How do I go about sending the employee_id to the company form page? Do I need to use some sort of redirect?
Flow: dashboard/employee_signup -> dashboard/company_signup -> completed_signup
I've looked through multiple tutorials on Django and most seem to be too simple to solve what I need done.
Here is my EmployeeSignUpView. Right now it redirects to a 'login' page. I need to instead redirect to a CompanySignUpView while passing along an employee_id. A company can't have zero employees, so the first person to signup for the company needs to be stored in the company model. The company table includes a column that stores a list of employees in that company. So a OneToMany relationship.
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
success_url = reverse_lazy('login')
template_name = 'employee_signup.html'
You redirect to something like
reverse('b:create_b', kwargs={"pk":a.pk}
The URL (in app 'b') is something like
url(r'create/(?P<pk>\d+)/$', b_create.as_view(), name='create_b'),
And you can pick up this parsed pk and convrt it into a full object by subclassing the dispatch method in b_create:
def dispatch( self, request, *a, **kw):
# resolve kwarg to self.a_object
self.a_object = get_object_or_404( A_model, pk = self.kwargs.get('pk','?') )
return super().dispatch( request, *a, **kw)
Doing this early means that the rest of your view code can everywhere reference self.a_object. It might be more efficient to do this only in post or form_valid, if it's not needed to (say) generate default values for the initial get of the form, or not needed until all the posted data is valid.
Instead of providing a static success_url, you can define get_success_url to return a URL that depends on the created object.
Assuming your CompanySignUpView has a URL as follows:
path('company_sign_up/<int:employee_id>/', views.CompanySignUpView.as_view(), name='company_sign_up')
then you would do:
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
template_name = 'employee_signup.html'
def get_success_url(self):
return reverse('company_sign_up', kwargs={'employee_id': self.object.id})
Edit
In your CompanySignUpView you can get the employee ID in the form_valid method:
class CompanySignUpView(CreateView):
...
def form_valid(self, form):
form.instance.initial_employee_id = self.kwargs['employee_id'] # or whatever the field name is
return super().form_valid(form)
I want to be able to save a form as a draft when it's not completely filled up and also save it as usual with classic django form validation.
To do so, I have two submit buttons in my form and I find out in my post request which button has been clicked :
class MyView(UpdateView):
def post(self, request, *args, **kwargs):
def submit_draft(self, request):
if 'draft' in request.POST:
out = True
else:
out = False
return out
In my models, all my field allow blank fields so saving an incomplete form as draft causes no issue if fields are empty.
I would like to make the form fields required when the form is saved with the normal save action.
One option I have thought of so far but didn't successfully implement :
=> Override get_form function so that when I hit Save as draft it just does it's normal action and when I hit Save, it modifies my fields required attribute.
tldr: I'm looking for a way to do something like that to my form based on the submit button that's been clicked
for field in form:
self.fields[field].required = True
Solved my problem by subclassing my forms as follows :
My form based on my model with null and blank = True
class RegionForm(forms.ModelForm):
class Meta:
model = Region
fields = ['region_name',
'region_description'
]
def __init__(self, *args, **kwargs):
super(RegionForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = True
class RegionDraftForm(RegionForm):
class Meta(RegionForm.Meta):
pass
def __init__(self, *args, **kwargs):
super(RegionDraftForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
That way I can instantiate in my views the form I need to save as draft or to normally save with complete form validation.
A solution I have used in the past is to serialise the content of the form into a "drafts" table when the "save draft" button is clicked.
When a user comes back to editing the form you load the json back into the form for continued editing. This can be done either using javascript or within the django form view.
Some steps you might follow:
When "Save Draft" is clicked, post the form to the draft view.
Serialise the content into json; storing the content in a "drafts model".
When a user click to re-open a draft pull the content from the database, parsing the json back into the form.
When the user clicks "Save" you save the form contents into the database and delete the draft from the "drafts table".
This is an example of what your Drafts model could look like.
class Drafts(models.Model):
user = models.ForeignKey(User)
form_name = models.CharField(max_length=100)
serialised_form = models.TextField() # could use json field here
def to_dict(self):
return json.loads(self.serialised_form)
I see this method having a number of advantages over
I am using Django REST as backend and angular as front end.
I have two serializers one for readong GET requests and other for POST, PUT requests.
This was because there are few fields like interval, etc which i am entering in integer by user but in the database i am saving as timedelta so i have to multitply them to make them as seconds on front end.
so e.g interval = 5 entered by user and i am posting 5*60*60 to server.
In order to read i have made ReadSerializer where i am diving that by 60*60 to again show to user what he added.
This is working find my problem is after saving my object to database the djnago rest frameework sends the object as it is saved which has interval = 5*60*60. inerval is just an example there 4-5 felds where i am changing them in front end before posting
Is there any way that response used my READ serializer before sending
class Serializer(serializers.ModelSerializer):
interval = serializers.SerializerMethodField('get_interval')
class Meta:
model = User
fields = ('id', 'interval')
def get_interval(self, obj):
return obj.interval/60*60
class WriteSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'interval')
This is the view
class UserListCreateView(generics.ListCreateAPIView):
queryset = User.objects.filter(is_deleted=False)
def get_serializer_class(self):
if self.request.method == 'GET':
return ReadSerializer
return WriteSerializer
You can use the transform_field method which allows you to customize the representation of a field when showing it.
Documentation about it: http://www.django-rest-framework.org/api-guide/serializers/#customizing-field-representation
Btw, careful if you are planning to upgrade to new versions of drf (>=3), these methods may disappear in favour of a unique to_representation method: https://groups.google.com/forum/?fromgroups#!topic/django-rest-framework/9kp5kXCssR4
I would just change it in JavaScript on the front end.
However I have a similar issue and solved it like this, (warning, I just started using DRF, so may not be the best way):
Just change the response of the method directly by first getting the original response, then editing the data property of it, which is a dict (pre-serialized) of the data to return:
class UserListCreateView(generics.ListCreateAPIView):
...
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
response.data['interval'] = response.data['interval'] / 60 / 60
return response