Create two models from one view django - django

That may seem obvious, but I cannot figure it out for a while. I have three models: Project, Question_Text, and Question. Question has ForeignKey to the Project and to the Question_Text. I can create and save Project, but when I'm creating Question_Text, I should also create a Question in my understanding, from one view. I tried to use Question.objects.create(), however, my dances around this seem fatal. I get the below error:
table project_question has no column named question_text_id
From DB point of view it's like this:
"question_text_id" integer NOT NULL REFERENCES "project_question_text" ("id")
My models:
class Project(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=40)
audience_count = models.IntegerField(default=0)
audience_ready = models.IntegerField(default=0)
pub_date = models.DateTimeField("Date Created")
def __unicode__(self):
return self.title
class Question_Text(models.Model):
text_en = models.CharField(max_length=60, blank=True)
class Question(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
question_text = models.ForeignKey(Question_Text)
viewed = models.BooleanField(default=False)
def __unicode__(self):
return self.question_text
My view:
def add_question(request, project_id):
a = Project.objects.get(id=project_id)
if request.method == "POST":
f = QuestionTextForm(request.POST)
if f.is_valid():
c = f.save(commit=False)
# c.pub_date = timezone.now()
q = Question.objects.create()
q.question_text = c
q.project = a
q.save()
c.save()
messages.success(request, "Your question was added")
return HttpResponseRedirect('/projects/get/%s' % project_id)
else:
f = QuestionTextForm(request.POST) #Add request params to initialization
args = {}
args.update(csrf(request))
# args['question'] = q
args['project'] = a
args['form'] = f
return render_to_response('project/add_question.html', args)
And my form:
class QuestionTextForm(forms.ModelForm):
class Meta:
model = Question_Text
fields = ('text_en',)
Any help is appreciated.

You are saving q before c. But as q has field (foreignkey) for c, c needs to be saved first. Django internally refers id of c to update the tables and rows appropriately.
So change your code to
c = f.save(commit=False)
# c.pub_date = timezone.now()
c.save() #save c
#prepare object
q = Question()
q.question_text = c
q.project = a
q.user = request.user
q.save()

Related

pass data from models.py to views.py and show it to user

I want to give users ten point each time they fill out one Survey , so i have this code above and now how to add the 10 point to self user after he fill out one
models.py :
class User(AbstractUser):
user_pic = models.ImageField(upload_to='img/',default="",null=True, blank=True)
coins = models.IntegerField(default=10)
def get_image(self):
if self.user_pic and hasattr(self.user_pic, 'url'):
return self.user_pic.url
else:
return '/path/to/default/image'
def give_coins(user, count):
user.coins = F('coins') + count
user.save(update_fields=('coins',))
user.refresh_from_db(fields=('coins',))
class Survey(models.Model):
name = models.CharField(max_length=200)
published_on = models.DateTimeField('Published DateTime')
def __str__(self):
return self.name
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.published_on <= now
was_published_recently.admin_order_field = 'published_on'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
class Participant(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
participation_datetime = models.DateTimeField('Participation DateTime')
def __str__(self):
return "Participant "+str(self.participation_datetime)
class Question(models.Model):
survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
question_text = models.CharField(max_length=200)
created_on = models.DateTimeField('Creation DateTime')
def __str__(self):
return self.question_text
views.py :
#register.inclusion_tag('survey/survey_details.html', takes_context=True)
def survey_details(context, survey_id):
survey = Survey.objects.get(id=survey_id)
return {'survey': survey}
#require_http_methods(["POST"])
def submit_survey(request):
form_data = request.POST.copy()
form_items = list(form_data.items())
print("form_items", form_items)
form_items.pop(0) # the first element is the csrf token. Therefore omit it.
survey = None
for item in form_items:
# Here in 'choice/3', '3' is '<choice_id>'.
choice_str, choice_id = item
choice_id = int(choice_id.split('/')[1])
choice = Choice.objects.get(id=choice_id)
if survey is None:
survey = choice.question.survey
choice.votes = choice.votes + 1
choice.save()
if survey is not None:
participant = Participant(survey=survey, participation_datetime=timezone.now())
participant.save()
return redirect('/submit_success/')
so what i must to do if i want to add 10 point to user after he complete one survey
If submit_survey is a call that requires authentication the user will be present on the request request.user.
Add the coins by adding request.user.give_coins(count=10) to the submit_query method.
you have 2 way
work with event driven tools(maybe hard but principled)
set give_coin befor participant.save() on submit_survey
anyway I din't notice, coin is on your absUser model but your Participant has nothing to do with it or relations

How can I obtain just one JSON object with the three models information combined?

How can I pass the foreign key values from my model to my serialised json object?
Now I have this three models,
class Fleet(models.Model):
fleet_id = models.IntegerField('Id flota', primary_key=True, unique=True)
fleet_name = models.CharField('Nombre flota', max_length=20, unique=True)
def __str__(self):
return self.fleet_name + ' ' + str(self.fleet_id)
class Device(models.Model):
dev_eui = models.CharField(max_length=16, primary_key=True, unique=True)
producer = models.CharField(max_length=20)
model = models.CharField(max_length=20)
dev_name = models.CharField(max_length=20, unique=True)
fleet_id = models.ForeignKey(Fleet, on_delete=models.CASCADE)
def __str__(self):
return self.dev_eui
class DevData(models.Model):
data_uuid = models.UUIDField(primary_key=True, default=uuid.uuid1, editable=False)
frequency = models.IntegerField()
data_1 = models.FloatField()
data_2 = models.FloatField(null=True, blank=True)
dev_eui = models.ForeignKey(Device, on_delete=models.CASCADE) #hay que saber porque aƱade _id
def __str__(self):
return self.dev_eui
And what I'm doing is call my view function in my JS code to obtain some data like this.
def getData(request):
ctx = {}
if request.method == 'POST':
select = int(request.POST['Select'])
data = DevData.objects.order_by('dev_eui','-data_timestamp').distinct('dev_eui')
nodes = Device.objects.all()
fleets = Fleet.objects.all()
data = loads(serializers.serialize('json', data))
nodes = loads(serializers.serialize('json', nodes))
fleets = loads(serializers.serialize('json', fleets))
ctx = {'Data':data, 'Nodes':nodes, 'Fleets':fleets}
return JsonResponse(ctx)
And inside my js file I filter it with some if else conditionals.
This works well, but I'm sure I can do it directly in my view but I don't know how. How can I obtain just one JSON object with the three models information combined?
Thank you very much!!
You can write a custom serializer like this:
from django.core.serializers.json import Serializer
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field == 'pk':
continue
elif field in self._current.keys():
continue
else:
try:
if '__' in field:
fields = field.split('__')
value = obj
for f in fields:
value = getattr(value, f)
if value != obj and isinstance(value, JSON_ALLOWED_OBJECTS) or value == None:
self._current[field] = value
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
Then use it like this
serializers = CustomSerializer()
queryset = DevData.objects.all()
data = serializers.serialize(queryset, fields=('data_uuid', 'dev_eui__dev_eui', 'dev_eui__fleet_id__fleet_name'))
I have wrote an article regarding serializing nested data here. You can check that out as well.

How to request object.id in form clean data funtion

Have a models:
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
variation_1 = models.BooleanField(default=True)
variation_2 = models.BooleanField(default=True)
variation_3 = models.BooleanField(default=True)
class Order(models.Model):
buyer = models.ForeignKey(User, related_name='buyer')
product = models.ForeignKey(Product)
variations_select = models.CharField(max_length=50, choices=VARIATIONS)
in forms.py
class OrderForm(forms.ModelForm):
variations_select = forms.ChoiceField(choices=VARIATIONS)
class Meta:
model = Order
fields = [
'variations_select'
]
i need to create a clean function that will check if variation_1 or variation_2 or variation_3 are availiable in Product. For this a need request Product.id to def clean_variations_select(self): How to do this?
def clean_variations_select(self):
product = Product.object.get(id = product.id)
variations_select = self.cleaned_data.get("variations_select")
if variations_select == "Variation_1" and product.variation_1 == False:
raise forms.ValidationError("variation_1 was sold already")
else:
return variations_select
product = Product.object.get(id = product.id) - don't work cause Order isn't created to database yet(but in rendering view link to new order creation i have that ../product.id/new_order).
You need to define an __init__ method for your class which should accept your product id as an argument.
def __init__(self,*args,**kwargs):
self.product_id = kwargs.pop('product_id')
super(OrderForm,self).__init__(*args,**kwargs)
When you initialize your form, you pass your product_id as a keyword argument.
OrderForm(request.POST, product_id=product_id)
And in clean method you can use self.product_id to get the Product object you want.
def clean_variations_select(self):
product = Product.object.get(id=self.product_id)
variations_select = self.cleaned_data.get("variations_select")
if variations_select == "Variation_1" and product.variation_1 == False:
raise forms.ValidationError("variation_1 was sold already")
else:
return variations_select

django custom manager with filter parameter

I would like to add a custom manager which can be called from a template, but does not affect the entire model (e.g. admin views) and which listens to a parameter set in the request (user_profile).
The following is what I have so far:
models.py:
class CurrentQuerySet(models.query.QuerySet):
def current(self):
return self.filter(id=1) ## this simplified filter test works..
class CurrentManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return CurrentQuerySet(self.model)
def current(self, *args, **kwargs):
return self.get_query_set().current(*args, **kwargs)
For model B is defined:
objects = CurrentManager()
The template calls:
{% for b in a.b_set.current %}
But as soon as I try to pass a parameter to that filter (in this case a date stored on the user-profile) the method does not return any results.
e.g.:
models.py
class CurrentQuerySet(models.query.QuerySet):
def current(self,my_date):
return self.filter(valid_from__lte=my_date)
showA.html
{% for b in a.b_set.current(request.user.get_profile.my_date) %}
Instead of passing the parameter from the template, I have also tried to set this in the view.py
#login_required
def showA(request,a_id):
my_date = request.user.get_profile().my_date
a = A.objects.get(id=a_id)
t = loader.get_template('myapp/showA.html')
c = RequestContext(request,{'a':a,'my_date':my_date,})
return HttpResponse(t.render(c))
Which part am I missing (or misunderstanding) here?
Thanks
R
Edit
Here the models. As mentioned, in this example it's a simple 1:n relationship, but can also be m:n in other cases.
class A(models.Model):
#objects = CurrentManager()
a = models.CharField(max_length=200)
description = models.TextField(null=True,blank=True)
valid_from = models.DateField('valid from')
valid_to = models.DateField('valid to',null=True,blank=True)
def __unicode__(self):
return self.a
class B(models.Model):
#objects = models.Manager()
objects = CurrentManager()
a = models.ForeignKey(A)
b = models.CharField(max_length=200)
screenshot = models.ManyToManyField("Screenshot",through="ScreenshotToB")
description = models.TextField(null=True,blank=True)
valid_from = models.DateField('valid from')
valid_to = models.DateField('valid to',null=True,blank=True)
def __unicode__(self):
return self.b
Edit-2
The accepted answer works for at least one relationship.
In case of a more nested data model, this method seems not to deliver the expected results:
models.py
class C(models.Model):
objects = CurrentManager()
b = models.ForeignKey(A)
c = models.CharField(max_length=200)
description = models.TextField(null=True,blank=True)
valid_from = models.DateField('valid from')
valid_to = models.DateField('valid to',null=True,blank=True)
def __unicode__(self):
return self.c
views.py
#login_required
def showA(request,a_id):
a = A.objects.get(id=a_id)
my_date = request.user.get_profile().my_date
b_objects = a.b_set.current(my_date)
c_objects = b_objects.c_set.current(my_date)
t = loader.get_template('controltool2/showA.html')
c = RequestContext(request,{'a':a,'b_objects':b_objects,'c_objects':c_objects,})
return HttpResponse(t.render(c))
This returns the error: 'QuerySet' object has no attribute 'c_set'.
I'd simplify it:
class CurrentManager(models.Manager):
def current(self, my_date):
return super(CurrentManager, self).get_query_set().filter(valid_from__lte=my_date)
and then use it like this:
a = A.objects.get(id=a_id)
my_date = request.user.get_profile().my_date
b_objects = a.b_set.objects.current(my_date)
and then just pass a to the template as the filtered objects accessing them using this:
{% for b in b_objects %}
Hope this helps!
Edit (by requestor):
I had to adjust it as follows to get it working:
a = A.objects.get(id=a_id)
my_date = request.user.get_profile().my_date
b_objects = a.b_set.current(my_date)
This threw an error: "'RelatedManager' object has no attribute 'objects'"
a.b_set.objects.current(my_date)

Django - How to do a transaction when saving a form?

I'm saving a form, but there is one save() method that is outside the transaction. The save() method outside a transaction is the save() on the "BicycleAdCategoryForm".
Here is the code:
models.py
class Main(models.Model):
section = models.ForeignKey(Section)
user = models.ForeignKey(User)
title = models.CharField(max_length=250)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.title
# To order in the admin by name of the section
class Meta:
ordering = ['date_inserted']
class BicycleAd(models.Model):
main = models.ForeignKey(Main)
bicycleadtype = models.ForeignKey(BicycleAdType)
bicycleaditemkind = models.ForeignKey(BicycleAdItemKind) # MPTT Model
bicycleadcondition = models.ForeignKey(BicycleAdCondition)
country = models.ForeignKey(GeonamesCountry)
city = models.ForeignKey(GeonamesLocal)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
# To order in the admin by name of the section
class Meta:
ordering = ['date_inserted']
class BicycleAdCategoryType(models.Model):
n_bicycle_ad_category_type = models.CharField(max_length=100) # COMPRA, VENDA, TROCA
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.n_bicycle_ad_category_type
# To order in the admin by name of the section
class Meta:
ordering = ['n_bicycle_ad_category_type']
forms.py
class MainForm(forms.ModelForm):
class Meta:
model = Main
exclude = ('user', 'section')
class BicycleAdForm(forms.ModelForm):
class Meta:
model = BicycleAd
exclude = ('main', 'bicycleadtype', 'bicycleaditemkind', 'bicycleadcondition', 'city') # DPS RETIRAR DAQUI A "CITY"
class BicycleAdCategoryForm(forms.ModelForm):
bicycleadcategorytype = forms.ModelMultipleChoiceField(queryset=BicycleAdCategoryType.objects.all(), required=False, widget=forms.CheckboxSelectMultiple) # Se retirar o widget fico uma SELECT box em q posso selecionar varias opcoes
class Meta:
model = BicycleAdCategory
exclude = ('bicyclead',)
def save(self, commit, rel_obj):
data = self.data.getlist('bicycleadcategorytype')
for item in data:
obj_bicycleadcategory = BicycleAdCategory()
obj_bicycleadcategory.bicyclead = rel_obj
obj_bicycleadcategory.bicycleadcategorytype = BicycleAdCategoryType.objects.get(pk=item)
obj_bicycleadcategory.save()
def clean_bicycleadcategorytype(self):
data = self.cleaned_data['bicycleadcategorytype']
try:
for item in data:
bicycleadcategorytype = BicycleAdCategoryType.objects.get(pk=item.pk)
return bicycleadcategorytype
except (KeyError, BicycleAdCategoryType.DoesNotExist):
raise forms.ValidationError('Invalid Bicycle Ad Category Type. Please try again.')
views.py
def submit_ad_view(request):
if request.method == 'POST':
model_main = Main()
model_main.section = Section.objects.get(pk=request.POST['section'])
model_main.user = request.user
model_bicyclead = BicycleAd()
model_bicyclead.bicycleadtype = BicycleAdType.objects.get(pk=2)
model_bicyclead.bicycleaditemkind = BicycleAdItemKind.objects.get(pk=4)
model_bicyclead.bicycleadcondition = BicycleAdCondition.objects.get(pk=2)
model_bicyclead.city = GeonamesLocal.objects.get(pk=4803854)
form_main = MainForm(request.POST, instance = model_main)
form_bicyclead = BicycleAdForm(request.POST, instance = model_bicyclead)
form_bicycleadcategory = BicycleAdCategoryForm(request.POST)
if form_main.is_valid() and form_bicyclead.is_valid() and form_bicycleadcategory.is_valid():
main_f = form_main.save()
bicyclead_f = form_bicyclead.save(commit=False)
bicyclead_f.main = main_f
bicyclead_f.save()
bicycleadcategory_f = form_bicycleadcategory.save(commit=False, rel_obj=model_bicyclead)
resultado = 'valid'
else:
resultado = 'n_valid'
return render_to_response('app/submit_ad.html', {'resultado': resultado}, context_instance=RequestContext(request))
I think main_f and bicyclead_f are inside a transaction but bicycleadcategory_f is outside a transaction. When bicycleadcategory_f fails, main_f and bicyclead_f are stored in the database.
Any clue on what I'm doing wrong?
Best Regards,
Django executes views using the commit_on_success decorator (or at least it behaves that way). If you're view crashes (uncaught exceptions), a rollback should take place. If some data is stored, and some is not there is a possibility that your DB engine does not support transactional processing.
Check out the django doc for more info
https://docs.djangoproject.com/en/dev/ref/databases/
For example, if you're using MySQL with MyISAM you may encounter some problems
edit:
Krzysiek Szularz: I guess everybody is using django TransactionMiddleware or simmilar things, so I skipped it - and mentioned only the logic layer.