Django form with dynamic fields and manytomany relationschips - python

I am a beginner Django user and have been stuck with this problem for weeks now.
Consider the following model
class Post(models.Model):
title = models.CharField(max_length=65)
author = models.ForeignKey(User)
date = models.DateField(auto_now_add=True)
content = models.TextField()
tags = models.ManyToManyField(Tag)
images = models.ManyToManyField(Image)
def __unicode__(self):
return self.title
I would like the User to be able to upload pictures to the with his model. I would like to be able to validate if
The user has at least uploaded one image and limit the maximum number of images to a certain number.
Basically do the same for tags. But with tags I would also like Django to check if the tags already exists and if so add it to the model.
Let the user push a button that says add Tag / add Image to make a new field pop up.
Problems I have encountered so far.
I am required to save a model before I can add many to many relations. This is a bit annoying because if an error occurs halfway down the road is might be possible that half the model is saved and the other is invalid.
I know that I can add extra field to the DOM using js however I got no clue how to process those in the View function.

For problem 1, this makes sense because if you create a model which is refered to by other models, said model key must exist. What I'd suggest you do is look into saving multiple models with a transaction.
2 is pretty easy, just use jQuery/Javascript to show/hide the appropriate fields in the browser based on the user's selection event.
Based on your comment, here's an example of how I handle data to and from the server
//Submit data to server, assuming you have already extracted out the relevant data values
$("some_button").click(function(e){
$.ajax({
url : "someUURLLocation/",
type : "POST",
data : {"data" : JSON.stringify({"field1" : var1, "field2" :var2}),
dataType : "json",
success : function(results){
if (results.success == "true") {
//handle DOM insertion and other stuff
} else
alert(results.message);
}
});
}
urls.py:
from django.conf.urls import patterns, url
from $APPNAME import views
urlpatterns = patterns("",
...
...
url(r'^someURLLocation/$', views.handleDataSubmission),
...
)
views.py:
from django.http import HttpResponse
from django.utils import simplejson
def handleDataSubmission(request):
data = simplejson.loads(request.POST.get("data", None))
if data is not None:
newPost = Post.objects.create( field1 = data["field1"], field2 = data["field2"])
dataReturn = [{"val1" : newPost.title, "val2" : newPost.date}]
return HttpResponse(simplejson.dumps({"success" : "true", "data" : dataReturn}), mimetype = "application/json")
else:
return HttpResponse(simplejson.dumps({"success" : "false", "message" : "Invalid data received by server"}), mimetype = "application/json")

Related

How to use initial in a form with multiple objects without getting MultipleObjectsReturned Error

Why is form throwing me an exception of MultipleObjectsReturned, when trying to let users edit their profile cover, I have a model for account and i also created a separate model to hold the upload of users profile cover images given that users can have multiple upload of a profile cover images. but somehow i happen to get this error (get() returned more than one AccountCover -- it returned 2!) when upload is more than one cover image.
cover = get_object_or_404(AccountCover, account=account.id).first()
if request.user:
forms = CoverImageForm(request.POST, request.FILES,instance=cover,
initial = {
'cover_image':cover.cover_image.url,
})
if forms.is_valid():
data = forms.save(commit=False)
data.account = cover.account
data.save()
else:
forms = CoverImageForm(
initial = {
'cover_image':cover.cover_image,
}
)
Here is my model for cover image .
class AccountCover(models.Model):
account = models.ForeignKey(Account,on_delete=models.CASCADE)
cover_image = models.ImageField(upload_to=get_cover_image_path,blank=True, null=True)
Form for the coverimage
class CoverImageForm(forms.ModelForm):
class Meta:
model = AccountCover
fields = ['cover_image']
I think that get_object_or_404 needs to return only one object. Try instead with:
from django.http import Http404
cover = AccountCover.objects.filter(account=account.id).first()
if not cover:
raise Http404()

Django How to add a model other than a form

I am making an app using Django2.0. But There are things that I do not know even if I investigate.
There are models as follows.
class Picture(models.Model):
place = models.ForeignKey(Place, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
image = models.ImageField(upload_to='image/')
using the following form
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['place', 'title', 'image']
And run following code on views.py
obj = Picture()
pic = PictureForm(request.POST, request.FILES, instance=obj)
pic.save()
I can add model peacefully. But in this way, I should select place and title on form every time. Beforehand I decide place and title, I will only select image on the form. It reduces my work.
Since there are several place and title, I do not want to set them to default values.
I succeeded in making a form.
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['image',]
But I don't know how to add title and place on views.py
I'm not good at English. Thank you for seeing the bad sentensecs to the end
Your issue comes from the view code, where you instantiate your new model.
If you want to create a new model you don't need to pass the insance argument, just edit the lines to get the following :
pic = PictureForm(request.POST, request.FILES)
pic.save()
See https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method
To be able to set only a few fields you should update your model declaration, with for example null=True, Blank=True, default=None in the optional fields arguments.
PS : the django documentation is transalated in many languages, but if you don't find yours try the IRC channels.
If I understand your problem clearly,
You have predecided the place and the title and want the user to enter only the image. Only image should be kept in the form, but you want to enter the title and the place in the view.If that's the case,do this :
Your view function:
def some_view(request):
if request.method == "POST":
T,P = get_title_and_place()
# This fuction will return a tuple with the title and the place that you need.
# Or Alternatively, you can pass the title and place as argument to the view
obj = Picture(title=T,place=P)
#Initialise object with title and place received from function or arguments
pic_form = PictureForm(request.POST, request.FILES)
#The form contains only the image field as in your last block of code
if pic_form.is_valid():
obj.image = request.FILES['image']
obj.save()
#Save the object
return redirect('some_url')
else:
return render('xyz.html',{"form":pic_form})
#Incoming image is invalid. Throw back the invalid form to the user
else:
#Incoming method is not POST, give user the form to fill the image
pic_form = PictureForm()
return render('xyz.html',{"form:pic_form})
On submission of the form from xyz.html, you should redirect back to this view. This is the reason why I made a seperate function to get the title and place so that user doesn't know about it via the url.
However be careful with this method. You have to take care of validating the title and place. Otherwise the obj.save() will throw error

several forms on the same page with django

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!")

How to use a queryset result to filter other queryset in DJango ORM?

I am trying to call a view using AJAX, but I have a problem. I have a token for the submit, the function that calls to Django view is working, I followed all the instructions of this link: https://realpython.com/blog/python/django-and-ajax-form-submissions/, but in the console I get the following error:
500: DoesNotExist at /Buscar/Producto/
InventarioProducto matching query does not exist.
/Buscar/Producto/ is the URL connected to the view, that is working, I think that is not the problem.
After importing the models, I tried the following in Django shell:
resp_producto=Producto.objects.filter(codigo_producto=9786071411532)
resp_inventario=InventarioProducto.objects.get(producto_codigo_producto__in=resp_producto)
resp_precio=Precio.objects.filter(producto_codigo_producto__in=resp_producto,estado_precio='1').order_by('-idprecio')[:1]
In the shell, if I print the variables were I saved the querysets, I can see the results, so i don't know why this is not working on the view.
9786071411532 is a product code that exist in the MySQL database, it is saved in a column named codigo_producto, which is the same name of a field saved in the model Producto, actually it is a primary key.
Explaining the models:
InventarioProducto has a field that is a foreigin key from Producto. The field in the model InventarioProducto is called producto_codigo_producto, and the primary key of Producto is called codigo_producto. So producto_codigo_producto referes to codigo_producto.
The model Precio has the same foreign key with the same name used in the model InventarioProducto, so they work in the same way.
Also I make sure that all the data that I'm requesting really exists.
Here is the view:
def BuscarProducto(request):
if request.method == 'POST':
txt_codigo_producto = request.POST.get('id_codigo_producto')
response_data = {}
resp_producto=Producto.objects.filter(codigo_producto=txt_codigo_producto)
resp_inventario=InventarioProducto.objects.get(producto_codigo_producto__in=resp_producto)
resp_precio=Precio.objects.filter(producto_codigo_producto__in=resp_producto,estado_precio='1').order_by('-idprecio')[:1]
response_data['result'] = 'Create post successful!'
response_data['codigoproducto'] = resp_producto.codigoproducto
response_data['lote'] = resp_inventario.idinventario_producto
response_data['descripcion_producto'] = resp_producto.descripcion_producto
response_data['precio'] = resp_precio.valor_precio
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
else:
return HttpResponse(
json.dumps({"nothing to see": "this isn't happening"}),
content_type="application/json"
)
I've moved the SOLVED text to an answer:
I changed this:
resp_inventario=InventarioProducto.objects.get(producto_codigo_producto__in=resp_producto)
To this:
resp_inventario=InventarioProducto.objects.filter(producto_codigo_producto__in=resp_producto)

Get all tags from taggit

How to get all the (unique) tags from django-taggit? I would like to display all the tags in a side bar.
Currently I am able to get all the tags for a particular post, but now I need to get all the unique tags in the entire blog.
code in models.py:
from django.db import models
from taggit.managers import TaggableManager
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
created = models.DateTimeField()
tags = TaggableManager()
You can use all() to get all the tags in your database:
from taggit.models import Tag
tags = Tag.objects.all()
If you need a complete solution, have a look at django-taggit-templatetags. It provides several templatetags, including one for tag list, to expose various taggit APIs directly to templates.
The currently maintained fork supporting newer versions of django is:
https://github.com/fizista/django-taggit-templatetags2
django-taggit-templatetags has not been maintained for some years.
I know this is an old question...but I'm a Django newbie and found this question while looking for a way to fill an Ajax dropdown with all tag options. I figured out a way with djangorestframework and wanted to put a more complete solution here for others (OP would also be able to populate a sidebar with the response, or do anything else with it).
This adds an api endpoint tag so you can not only view them by navigating to /tag/ but get a JSON response suitable for Ajax (ergo, this assumes you have djangorestframework installed and in use).
serlializers.py
from taggit.models import Tag
class MyTagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['name', 'slug']
views.py
from taggit.models import Tag
class TagViewSet(viewsets.ModelViewSet):
"""
Not using taggit_serializer.serializers.TaggitSerializer because that's for listing
tags for an instance of a model
"""
queryset = Tag.objects.all().order_by('name')
serializer_class = MyTagSerializer
urls.py
router.register(r'tag', TagViewSet)
And, if you need the ajax:
$(document).ready(function(){
$.ajax({
url : "/tag/",
dataType: "json",
type: 'GET'
}).done(function(response){
for (var i in response) {
tagEntry = response[i];
$('#tagdropdown').append($('<option>', {
value: tagEntry.name,
text: tagEntry.name
}));
}
}).fail(function(response){
console.log('failed!');
console.log(response);
});
});

Categories