I would like to do the following:
With my model
class User(models.Model):
id = models.AutoField(primary_key=True)
field1 = models.CharField(max_length=100)
fk1 = models.ForeignKey(Group)
goes on
After this, I created my Serializer, which looks like:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserSerializer
fields = (...)
...
Lastly, I create my ViewSet, which should look like this:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
but now, how would I be able to create a viewSet and, for example, generate a form with my class? My final idea is to be able to do something like:
def my_view(request):
my_form = UserViewSet.as_view({'get': 'list'}(request))
# from here either to be able to use .render().content
# or, inside the template, render with {% render_form my_form %}
Is this possible? Thank you
What you're suggesting is possible, but it doesn't usually make sense. Django Rest Framework is great for creating an API so that you can send and receive information to and from that api, maybe in JSON format.
Getting a form involves getting rendered HTML from the server. That's a better task for Django itself. You could use a CreateView to do what you're trying to:
def UserCreateView(CreateView):
model = User
form_class = "name_of_your_form.html" # Or you could call the form 'user_create_form.html` and leave this line out.
fields = [...]
Your form will then be available in your template, per the docs:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
As docs says:
Serializers may be rendered as forms by using the render_form template tag, and including the serializer instance as context to the template.
Next example will probably work for you. But how #YPCrumble sayed DRF goal is easy and fast build an API, not rendering forms
class UserDetail(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'your_template.html'
def get(self, request, pk):
serializer = UserSerializer(profile)
return Response({'serializer': serializer})
def post(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(data=request.data)
if not serializer.is_valid():
return Response({'serializer': serializer})
serializer.save()
return redirect('some_url_name')
template file:
{% load rest_framework %}
<html><body>
<form action="{% url 'user_url' pk=user.pk %}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
</body></html>
`DRF Docs link
Related
I use these two classes to get the context data and check permissions respectively:
class RestaurantView(View):
def get_context_data(self):
ctx = {'restaurant': get_object_or_404(Restaurant, slug=self.kwargs['slug'])}
return ctx
class ManageRestaurantMixin(LoginRequiredMixin, UserPassesTestMixin, RestaurantView):
def test_func(self):
ctx = super().get_context_data()
return ctx['restaurant'].owner == self.request.user
Now the first one is working, so when i don't need permission i get the expected behavior, for example with this DetailView:
class Info(RestaurantView, DetailView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/info.html'
But then if inherit from ManageRestaurantMixin the permissions are checked as expected, but the context object is not working and the template displays an empty form:
class EditInfo(ManageRestaurantMixin, UpdateView):
model = Restaurant
context_object_name = 'restaurant'
template_name = 'restaurant/edit_info.html'
fields = ['name', 'address', 'logo']
success_url = '/account/myrestaurants'
I get that the context gets overwritten, but i don't get how.
How can i solve this?
Is there a better way to handle this kind of situations?
Edit:
edit_info.html:
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input class="btn btn-primary" type="submit" value="update">
</form>
{% endblock %}
My Views is :
class TweetsListApiView(generics.ListCreateAPIView):
queryset = Tweet.objects.all()
serializer_class = TweetListSerializer
renderer_classes = [TemplateHTMLRenderer]
template_name = 'components/form.html'
Serializers:
class TweetListSerializer(serializers.ModelSerializer):
likes = serializers.SerializerMethodField('get_likes')
class Meta:
model = Tweet
fields = ['id','content','likes']
def get_likes(self,request):
return random.randint(0,9999)
form.html
{% load rest_framework %}
<form method='Post'>
{% csrf_token %}
{% render_form serializer %}
<button type="submit" class="btn btn-secondary">Add</button>
</form>
urls.py:
path('tweets/',TweetsListApiView.as_view()),
Is it possible to have from one viewclass class TweetsListApiView
standart Rest Page and HTML page where I creating new Tweet. if no
what the best way to create this html using serializer and Django REST
I have this HTML template
<html><body>
<form method="POST">
{% render_form submitSerializer %}
<input type="submit" value="Save">
</form>
</body></html>
this serializer
class SubmitSerializer(serializers.ModelSerializer):
submittedFile = serializers.FileField()
class Meta:
model = Submit
fields = {'submittedFile', 'result'}
read_only_fields = {'result'}
and this view
class SubmitView(APIView):
renderer_classes = [renderers.TemplateHTMLRenderer]
def get(self, request, *args, **kwargs):
return Response({'submitSerializer':submitSerializer}, template_name='singlelesson.html')
def post(self, request, *args, **kwargs):
submit = Submit(lesson=lessonInstance, user=self.request.user, submittedFile = self.request.data['submittedFile'])
serializer = SubmitSerializer(submit)
#compare_files returns true if two files contain same text
result = compare_files(correct_solution, self.request.POST.get('submittedFile'))
serializer.save(result = result)
return Response({'submitSerializer':serializer}, template_name='singlelesson.html')
Problem is that the form uploads not the file but its name and I get type error when comparing the two files. How do I make the form upload the file instead?
You should extract the file from the FILES QueryDict in the request object, not POST. e.g.:
self.request.FILES['submittedFile']
Or the django-rest-framework way:
request.data['submittedFile']
For more explanation check out:
http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser
EDIT: Also change form to:
<html><body>
<form method="POST" enctype="multipart/form-data">
{% render_form submitSerializer %}
<input type="submit" value="Save">
</form>
</body></html>
And SubmitView to:
class SubmitView(APIView):
parser_classes = (MultiPartParser,)
...
My ValidationError seems to be stuck somewhere!
The routine clean() seems to be running as expected since I am not being forwared to success_url, but the error message does not show up.
forms.py
from django import forms
from django.core.exceptions import ValidationError
class ContactForm(forms.ModelForm):
def clean(self):
raise forms.ValidationError('basic error')
class Meta:
model = Person
views.py
class ContactView(generic.edit.FormView):
def form_valid(self, form):
template_name = 'union/contact.html'
form_class = ContactForm # class of union/forms.py
success_url = '/union/VIP_confirm'
context_object_name = 'contact'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
context['count'] = countcal()
return context
def form_valid(self, form):
if True:
model_id = form.save()
person_id = model_id.pk
return HttpResponseRedirect(
reverse('union:VIP_save', kwargs={'person_id': person_id}))
union/contact.html template for the ContactView
<form action="{% url 'union:ContactView' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
<input type="submit" value="abschicken" />
<!-- regular value submission -->
</form>
I followed the official docs and several other sources but to no success as to find out what the problem is or where it lies. To solve this, do you need additional infos about my app?
EDIT: I seem to miss something in the template, since the test error message (basic error) shows up if the template just uses the form-placeholder {{form}}
Using Django 1.7.1
EDIT EDIT:
OK. The comments pointed me to the right direction. I missed {{form.errors}}.
So I'm new to django and I'm struggling with the documentation for class based views. Not really sure what I'm doing wrong here, but this is the problem:
I fill in the appropriate data on the form, click submit and I'm redirected to the same url plus some get parameters that correspond to what I submitted in the form. Nothing new is added to the db. I would like to create a new Advertisement row in the db when I click submit.
I have a ModelForm as follows:
class NewAdForm(ModelForm):
class Meta:
model = Advertisement
exclude = ('campaign',)
def __init__(self, campaign, *args, **kwargs):
super(NewAdForm, self).__init__(*args, **kwargs)
self.campaign = campaign
I also have a FormView:
class AddAdvertView(FormView):
form_class = NewAdForm
template_name = 'advertisers/newad.html'
def get_form_kwargs(self):this
kwargs = super(AddAdvertView, self).get_form_kwargs()
kwargs['campaign'] = get_object_or_404(Campaign, id__exact = self.kwargs['campaign_id'])
return kwargs
def form_valid(self, form):
form.save(commit = True)
return super(AddAdvertView, self).form_valid(form)
And here's the template:
<form action="" method="get">
{{ form.as_p }}
<input type="submit" value="Submit"/>
</form>
Any reason why you're using method="get"? The FormView class is probably expecting a POST in order to trigger validation and saving. GET is usually just used to render the initial form.
It's possible that it's because you're missing the CSRF token, but it really should give you an error about that. Still, try:
<form action="" method="get">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit"/>
</form>
Also, while it doesn't explain why the form isn't saved, you need to specify a success_url on your FormView, the URL the user should be redirected to after successfully submitting the form. I've actually never tried leaving it off, but it's possible that the view is taking you back to the form simply because it doesn't know where else to go.