I have been trying to make a Form for item removal but I don't know how to connect the field to the model, here's what I'm doing:
class StudentForm(forms.ModelForm):
queryset = Student.objects.filter().values('name')
choices = [('', var) for var in queryset]
names = forms.ChoiceField(choices=choices)
class Meta:
model = Student
fields = '__all__'
I used this class to connect to the Student model and use its fields, however I want to add a field of my own to it, which is names = forms.ChoiceField(choices=choices), but what I want to know is, how would I connect this field that lists all names, for example, to the form to make it so that I could pick an object's name and then I could change/delete it accordingly?
class StudentRegister(generic.FormView):
template_name = 'students/student_form.html'
form_class = StudentForm
success_url = '/'
def form_valid(self, form):
form.save(commit=True)
return super().form_valid(form)
This is my views.py and as you can see, it automatically sets the values of the form because those are already "tied in" to a model field, but not the choice field I added. How would I correct this?
1) Approach using queryset
class StudentForm(forms.ModelForm):
name = forms.ChoiceField(queryset=Student.objects.all())
or
name = forms.ChoiceField([("%s" % stud['id'], "%s" % stud['name']) for stud in
Student.objects.all().values('id', 'name')])
class Student(models.Model):
# existing fields comes here
def __str__(self):
return self.name
2) Dynamic load using ajax
class StudentForm(forms.ModelForm):
name = forms.ChoiceField()
class Student(models.Model):
# existing fields comes here
def __str__(self):
return self.name
Load data using ajax
<script>
(function($){
//get your data
function get_data(){
$("#<id_of_choice_field>").empty();
$.ajax ({
type: "POST",
url: "<url_for_getting_data>",
data: { "csrfmiddlewaretoken": "{{ csrf_token }}" },
cache: false,
success: function(json) {
if (json) {
for (var source in json) {
$("#id_of_choice_field").prepend("<option value='"+json[source].id+"'>"+json[source].name+"</option>");
}
}
}
});
$("#id_of_choice_field").prepend("<option value='' selected='selected'>--------------</option>");
}
get_data();
}(django.jQuery));
</script>
AJAX URL & method
url(r'^get-all-student/$', get_all_student)
def get_all_student(request):
"""
:param request:
:return:
"""
if request.method == "POST" and request.is_ajax():
all_student = []
for student in Student.objects.all().values('id', 'name'):
all_student.append({'id': student['id'], 'name': student['name']})
return HttpResponse(json.dumps(all_student), content_type="application/json")
return HttpResponseNotAllowed(['GET'])
You can pass all the names to a single field with:
class StudentForm(forms.ModelForm):
name = forms.ModelMultipleChoiceField(queryset=Student.objects.all())
class Meta:
model = Student
fields = '__all__'
and let your Student model return the name:
class Student(models.Model):
# Model fields
def __str__(self):
return self.name
You can look for more ways for your forms to interact with models at https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#overriding-the-default-fields
Related
I am using serializers.Serializer instead of ModelSerializer which doesn't require Meta class but it keep saying object has no attribute Meta. Iam not sure what is the issue but when I run the localserver, the main page gives error saying api fetch error and in the terminal it says AttributeError: 'Serializer' object has no attribute 'Meta'.
My view:
class ClassView(viewsets.ModelViewSet):
queryset = Class.objects.all().order_by('-created_at')
serializer_class = ClassSerializer
serializer_action_classes = {
'get_all_students_of_a_class': ClassDetailSerializer,
}
# .annotate(total_students=Count('students_in_class'))
def get_serializer_class(self):
"""
returns a serializer class based on the action
that has been defined.
"""
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super(ClassView, self).get_serializer_class()
def get_all_students_of_a_class(self,request,pk=None):
"""
returns a class details with all the students signed up for the
class along with the subject.
"""
id = pk
if self.is_teacher(id):
online_classes = get_object_or_404(Class, id=id)
students = Student.objects.filter(online_class__id=id)
subject_details = Subject.objects.get(online_class__id=id)
total_students_in_class = online_classes.total_students_in_class
created_at = online_classes.created_at
updated_at = online_classes.updated_at
data = {
"teacher": self.get_teacher_instance(),
'total_students_in_class': total_students_in_class,
"students_in_class": students,
"subject": subject_details,
'created_at': created_at,
'last_updated_at': updated_at,
}
serializer_class = self.get_serializer_class()
serializer = serializer_class(data)
return Response(serializer.data, status=status.HTTP_200_OK)
My serializer:
class ClassDetailSerializer(serializers.Serializer):
teacher = serializers.StringRelatedField()
subject = SubjectLevelSerializer()
total_students_in_class = serializers.ReadOnlyField()
students_in_class = serializers.StringRelatedField(many=True)
created_at = serializers.DateTimeField(read_only=True)
last_updated_at = serializers.DateTimeField(read_only=True)
My url:
path("class/<int:pk>/",teacher.ClassView.as_view({"get": "get_all_students_of_a_class","delete":"destroy"}),
),
However it works and I can perform action if I go to localhost/admin and other api calls from localhost.
Add a Meta class with model = YourModel
class ExampleDetailSerializer(serializers.Serializer):
employee = serializers.StringRelatedField()
person = PersonSerializer()
class Meta:
model = Example # model name
fields = ('__all__')
Try to use "ViewSet" instead of "ModelViewSet". And while using ViewSet make sure to define list, create, etc. function by your own.
Lets say I have these models:
class Download(MPTTTimeStampedModel):
endpoint = models.ForeignKey(EndPoint, related_name="downloads",)
class EndPoint(TimeStampedModel):
name = models.CharField(max_length=100, verbose_name=_(u"Nombre"))
url = models.CharField(max_length=2000, verbose_name=_(u"Url"))
These serializers:
class DownloadSerializer(serializers.ModelSerializer):
class Meta:
model = Download
fields = ('id', 'endpoint')
def create(self, validated_data):
...
def update(self, validated_data):
...
class EndPointSerializer(serializers.ModelSerializer):
class Meta:
model = EndPoint
fields = ('id', 'name', 'url')
def create(self, validated_data):
...
def update(self, validated_data):
...
And this generic api view:
class DownloadList(generics.ListCreateAPIView):
queryset = Download.objects.all()
serializer_class = DownloadSerializer
This will allow me to create a download by sending a json representation looking like this:
{
'id': null,
'endpoint': 19
}
And upon creation, the web service will send me back the data with the id from the database. Now, I actually want the web service to send me back not just the endpoint id but a complete representation of the object, Something like this:
{
'id': null,
'endpoint': {
'id': 19,
'name': 'my endpoint',
'url': 'http://www.my-endpoint.com/'
}
}
I would manage this with this serializer:
class DownloadDetailedSerializer(DownloadSerializer):
endpoint = EndPointSerializer(many = False, read_only=False)
And now the actual question: how do i tell my generic view to use this last serializer for the returned data while keeping the original DownloadSerializer for the input?
EDIT: as #neverwalkeralone suggested the way to go is creating a custom field and overriding to_representation method. But that lead me to an exception in the line serializer = EndPointSerializer(value), and after some testing I found out that it would be better to inherit my custom field from RelatedField. That implies overriding to_internal_value too. So here is what finally got the job done:
class EndPointField(serializers.RelatedField):
def to_representation(self, value):
serializer = EndPointSerializer(value)
return serializer.data
def to_internal_value(self, instance):
endpoint = EndPoint.objects.get(pk=instance)
return endpoint
def get_queryset(self):
return EndPoint.objects.all()
You can define custom field, use to_representation method to customize output format:
class EndPointField(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
serializer = EndPointSerializer(value)
return serializer.data
def get_queryset(self):
return models.EndPoint.objects.all()
And use it in DownloadSerializer for endpoint field:
class DownloadSerializer(serializers.ModelSerializer):
endpoint = EndPointField()
class Meta:
model = Download
fields = ('id', 'endpoint')
UPD
Based on Kilian Perdomo Curbelo feedback EndPointField's to_representation value should be replaced with endpoint instance:
def to_representation(self, value):
endpoint = EndPoint.objects.get(pk=value.pk)
serializer = EndPointSerializer(endpoint)
return serializer.data
I'm trying to build forms linked to a PostgreSQL database using Django ModelForms. The template is rendering two of the fields(the ones with ManyToMany relationships), but it only gives me an empty box for "title".
This is my forms.py:
Forms.py:
class ProgramForm(forms.ModelForm):
class Meta:
model = Program
fields = ['function','task', 'title']
widgets = {
'function' : forms.Select,
'task' : forms.Select,
'title' : forms.Select,
}
This is my Models.py:
class Program(models.Model):
title = models.CharField(max_length=255)
function = models.ManyToManyField(function, related_name='programs')
task = models.ManyToManyField(Task, related_name='programs')
def __unicode__(self):
return self.title
class Task(models.Model):
tasknum = models.CharField(max_length=20)
taskname = models.CharField(max_length=100)
task_num_name = models.CharField(max_length=100)
function = models.ForeignKey(Function, related_name="tasks")
def __unicode__(self):
return self.task_num_name
class Function(models.Model):
function = models.CharField(max_length=50)
function_abrev = models.CharField(max_length = 25)
def __unicode__(self):
return self.function
Views.py:
def main(request):
return render (request, 'assignments/main.html')
def add_program(request):
form = ProgramForm()
return render (request, 'assignments/ad_form.html', {"form":form})
def link(request):
if request.method == 'POST':
form = ProgramForm(request.POST)
if form.is_valid():
return HttpResponse("we maybe getting somewhere")
else:
return HttpResponse("keep working")
I need a couple of things to happen:
I need for the "title" to render in the html page as a scroll down(the same way "function" and "task" appear.
I need to be able to save the relationships. The models are populated with all the information required with the exception of the relationships. The objective is for a staff member to be able to chose a "function", for that choice to act as a filter for the "task" scroll down(function and task have a OneToMany), and then allow them to choose any programs they want to add to their portfolio.
Any help will be much appreciated.
1. Title field in form
For this, I don't quite understand how the title field could be a scroll down the same way function and task are. Function and task are drop downs because they are manytomany fields linked to other models, meaning that the user has to pick which other objects in the Functions model and the Tasks model are to be linked. The title field, on the other hand, is just a CharField and so there is no defined set of things for the user to pick from. To allow the user to enter in the title for the Program, you should change the widget for title to Textarea() as such:
forms.py
from django.forms import ModelForm, Textarea
class ProgramForm(forms.ModelForm):
class Meta:
model = Program
fields = ['function','task', 'title']
widgets = {
'function' : forms.Select,
'task' : forms.Select,
'title' : Textarea(),
}
2. Save the Program from the form
To save the Program created by the user on staff member, simply add form.save() to your link(request) function:
views.py
def link(request):
if request.method == 'POST':
form = ProgramForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse("we maybe getting somewhere")
else:
return HttpResponse("keep working")
Hope this helps!
I was able to do a query from views.py and pass if to the template.
Views.py
def function_page(request, Function_id):
assignments = Function.objects.get(id=Function_id)
programs = assignments.programs.all()
context = {
'assignments': assignments,
'programs' : programs
}
return render (request, 'function.html', context)
HTML
{% for program in programs %}
<option value="{{program.title}}">{{program.title}}</option>
{% endfor %}
According to my very little knowledge, whenever we make a form, it extracts the form from a table and saves it to the same table.
for example:
class ExampleForm(forms.Form):
class Meta:
model=Example
Here the ExampleForm is formed from Example model and once submitted it is saved back to Example model.
What i want is, I want to develop a form from a table and save it to another table?
Is this possible?
Why do i want this?
Because, i am creating a form, that contains only checkboxes and labels of these checkboxes are retrieved from a table, but i want to save the user input into a different table
models.py
class Offer(models.Model):
package = models.ForeignKey(Package)
discount = models.CharField(max_length=120,null=True,blank=True)
transportation = models.CharField(max_length=120,null=True,blank=True)
nextTrip = models.CharField(max_length=120,null=True,blank=True)
rewardPoints = models.IntegerField(max_length=3,null=True,blank=True)
refferals = models.CharField(max_length=3, choices=REFERRAL_CHOICES)
def __str__(self):
return self.package.packageTitle
Offer model is used to create the form, but once the data is submitted, it should be saved in OfferRequest model
class OfferRequest(models.Model):
offerReq = models.ForeignKey(Offer,null=True)
userReq = models.ForeignKey(User)
discountReq = models.BooleanField(default=False)
transportationReq = models.BooleanField(default=False)
nextTripReq = models.BooleanField(default=False)
rewardPointsReq = models.BooleanField(default=False)
refferalsReq = models.BooleanField(default=False)
def __str__(self):
return self.discountReq
forms.py
class OfferRequestForm(forms.ModelForm):
discountReq = forms.BooleanField(required=False,label="Discount")
transportationReq = forms.BooleanField(required=False,label="Transportation")
nextTripReq = forms.BooleanField(required=False,label="Next Trip")
rewardPointsReq = forms.BooleanField(required=False,label="Reward Points")
refferalsReq = forms.BooleanField(required=False,label="Refferals")
class Meta:
model = OfferRequest
fields = ("discountReq","transportationReq","nextTripReq","rewardPointsReq","refferalsReq")
Since i am making the form from OfferRequest, it is rendered as shown below:
() discount
() transportation
() nextTrip
() refferals
() rewardPoints
but what i want is :
() discount : 3%
() transportation :FREE
() nextTrip :20%
() refferals : Yes
() rewardPoints : 200
The above form output can be rendered if the form is derived from Offer model, because the different offers are defined in the Offer model
Now when this form (derived from Offer) is submitted, i want it to get saved in OfferRequest
Trying my best to explain, do ask if something not clear.
If you want to add Offer's values to the field labels of the OfferRequestForm then you should override the __init__() method:
class OfferRequestForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
offer = kwargs.pop('offer')
super(OfferRequestForm, self).__init__(*args, **kwargs)
for field in offer._meta.fields:
field_name = field.name
form_field_name = field_name + 'Req'
if form_field_name in self.fields:
self.fields[form_field_name].label += \
u': %s' % getattr(offer, field_name)
So now you have to pass the offer argument to the OfferRequestForm's constructor:
def offer_req(request, offer_id):
offer = get_object_or_404(Offer, pk=offer_id)
if request.method == 'POST':
form = OfferRequestForm(request.POST, offer=offer)
if form.is_valid():
offer_request = form.save(commit=False)
offer_request.offerReq = offer
offer_request.userReq = request.user
offer_request.save()
return redirect(offer_request)
else:
form = OfferRequestForm(offer=offer)
return render(request, 'offer_req.html', {'form': form})
models.py:
class Tag(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now_add=True)
class Post(models.Model):
user = models.ForeignKey(User)
tag = models.ManyToManyField(Tag)
title = models.CharField(max_length=100)
content = models.TextField()
created = models.DateTimeField(default=datetime.datetime.now)
modified = models.DateTimeField(default=datetime.datetime.now)
def __unicode__(self):
return '%s,%s' % (self.title,self.content)
class PostModelForm(forms.ModelForm):
class Meta:
model = Post
class PostModelFormNormalUser(forms.ModelForm):
class Meta:
model = Post
widgets = { 'tag' : TextInput() }
exclude = ('user', 'created', 'modified')
def __init__(self, *args, **kwargs):
super(PostModelFormNormalUser, self).__init__(*args, **kwargs)
self.fields['tag'].help_text = None
views.py:
if request.method == 'POST':
form = PostModelFormNormalUser(request.POST)
print form
print form.errors
tagstring = form.data['tag']
splitedtag = tagstring.split()
if form.is_valid():
temp = form.save(commit=False)
temp.user_id = user.id
temp.save()
l = len(splitedtag)
for i in range(l):
obj = Tag(name=splitedtag[i])
obj.save()
post.tag_set.add(obj)
post = Post.objects.get(id=temp.id)
return HttpResponseRedirect('/viewpost/' + str(post.id))
else:
form = PostModelFormNormalUser()
context = {'form':form}
return render_to_response('addpost.html', context, context_instance=RequestContext(request))
Here form.is_valid() is always false because it gets the tag as string from form. But it expects list as form.data['tag'] input. Can anyone tell me how can i fix it?
How can i write a custom widget to solve this?
I don't think you need a custom widget (you still want a TextInput), you want a custom Field. To do this, you should subclass django.forms.Field. Unfortunately the documentation is scant on this topic:
If the built-in Field classes don’t meet your needs, you can easily create custom Field classes. To do this, just create a subclass of django.forms.Field. Its only requirements are that it implement a clean() method and that its init() method accept the core arguments mentioned above (required, label, initial, widget, help_text).
I found this blog post that covers both custom widgets and fields in more depth. The author disagrees with the documentation I quoted above - it's worth reading over.
For your specific situation, you would do something like this (untested):
class MyTagField(forms.Field):
default_error_messages = {
'some_error': _(u'This is a message re: the somr_error!'),
}
def to_python(self, value):
# put code here to coerce 'value' (raw data from your TextInput)
# into the form your code will want (a list of Tag objects, perhaps)
def validate(self, value):
if <not valid for some reason>:
raise ValidationError(self.error_messages['some_error'])
Then in your ModelForm:
class PostModelFormNormalUser(forms.ModelForm):
tag = MyTagField()
class Meta:
model = Post
exclude = ('user', 'created', 'modified')
def __init__(self, *args, **kwargs):
super(PostModelFormNormalUser, self).__init__(*args, **kwargs)
self.fields['tag'].help_text = None