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)
Related
I tried to use generic relation in django project. but it gives attribute error.
Generic relation is between UniqueSourcePresenter and UniqueDbSource
i have an instance of TextTable which has a foreignkey attribute for UniqueSourcePresenter i tried to use reverse relation as self.presenter.texttable in UniqueDbSource's instance method. but it gives error es below
File "/usr/src/app/apps/tableau/models/source.py", line 191, in create_datum
backend | if self.presenter.texttable is not None:
backend | AttributeError: 'GenericRelatedObjectManager' object has no attribute 'texttable'
My models are like follows
class UniqueSourcePresenter(models.Model):
"""
Generic Relation Manager of all types of source
"""
# Below the mandatory fields for generic relation
limit = models.Q(app_label = 'tableau', model = 'UniqueDbSource'.lower()) | models.Q(app_label = 'tableau', model = 'UniqueAPISource'.lower())
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to = limit)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
pass
class DataSource(models.Model):
pass
class UniqueSource(DataSource):
presenter = GenericRelation(UniqueSourcePresenter)
class Meta:
abstract = True
class UniqueDbSource(UniqueSource):
"""
THIS CLASS ADDED TO CONTENTTYPE
"""
def create_datum(self):
""" THIS METHOD CALLED WITH SIGNAL """
d = DatumDb()
d.title = f"{self.title}_datum"
if self.presenter.texttable is not None: ## THIS LINE GIVES ERROR
d.connector = self.presenter.texttable.init_source.data.connector
query = self.presenter.texttable.init_source.data.query
elif self.presenter.charttable is not None:
d.connector = self.charttable.presenter.init_source.data.connector
query = self.charttable.presenter.init_source.data.query
query.pk = None
query.save()
d.query = query
d.save()
self.data = d
pass
def create_presenter(self):
p = UniqueSourcePresenter()
p.content_type = ContentType.objects.get(app_label='tableau', model='UniqueDbSource'.lower())
p.object_id = self.id
p.save()
class Table(models.Model):
unique_source = models.OneToOneField(UniqueSourcePresenter, on_delete=models.CASCADE, null=True, blank=True, related_name="%(class)s")
class Meta:
abstract = True
class TextTable(Table):
pass
def create_unique_source(self):
"""
create a unique source object if created
and add selfs
"""
if self.init_source.content_type.model == 'InitialDbSource'.lower():
us = UniqueDbSource()
us.title = f"{self.title}_unique_source"
us.save()
self.unique_source = us.presenter
elif self.init_source.content_type.model == 'InitialAPISource'.lower():
us = UniqueAPISource()
us.title = f"{self.title}_unique_source"
us.save()
self.unique_source = us.presenter
pass
I added pass words where is not about my problem.
EDIT:
signal.py
#receiver(post_save, sender=UniqueDbSource)
#receiver(post_save, sender=UniqueAPISource)
def after_unique_source_save(sender, instance, created, **kwargs):
if created:
instance.create_datum()
instance.create_presenter()
instance.__class__.objects.filter(id=instance.id).update(
data=DatumDb.objects.all().last())
else:
pass
#receiver(post_save, sender=TextTable)
def after_table_save(sender, instance, created, **kwargs):
if created:
pass
instance.create_unique_source()
pass
if created:
TextTable.objects.filter(id=instance.id).update(
unique_source=UniqueSourcePresenter.objects.all().last()
) # may you can also give a good advice for this line
I have manytomanyfield inside my model.The manytomanyfield field lists the products in the products table.
I want to enter the amount for each product I choose. How can I relate manytomanyfield to floatfield field?
That's my model:
`
class TaskSources(models.Model):
id = models.AutoField(primary_key=True)
user_task_id = models.ForeignKey(UserTask,on_delete=models.CASCADE)
product_id = models.ManyToManyField(Product, verbose_name="Product",default=None)
product_amount = models.FloatField(max_length=255,verbose_name="Product Amount")
`
The form:
`
class TaskSourcesForm(forms.ModelForm):
class Meta:
model = TaskSources
fields = ['product_id', 'product_amount']
`
The views:
`
#login_required(login_url="login")
def addUserTask(request):
user_task_form = UserTaskForm(request.POST or None,initial={'user_id': request.user})
task_sources_form = TaskSourcesForm(request.POST or None)
if request.method == 'POST':
if user_task_form.is_valid():
user_task = user_task_form.save(commit=False)
user_task.author = request.user
user_task.save()
print(user_task.id)
if task_sources_form.is_valid():
task_sources = task_sources_form.save(commit=False)
task_sources.user_task_id = UserTask(id = user_task.id)
task_sources.save()
task_sources_form.save_m2m()
messages.success(request,"Task added successfully!")
return redirect(".")
context = {
"user_task_form" : user_task_form,
"task_sources_form" : task_sources_form,
}
return render(request,"user/addtask.html",context)
`
Thanks for care.
I tried associating the two fields with each other, but I could not succeed.
If I got it right I think that what you need is an intermediate table between your models. That way you can link an amount of a product to a TaskSource, something similar to this:
class Product(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class ProductAmount(models.Model):
amount = models.FloatField(max_length=255,verbose_name="Product Amount")
product = models.ManyToManyField(Product, through='TaskSources')
def __str__(self):
return self.name
class UserTask(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class TaskSources(models.Model):
id = models.AutoField(primary_key=True) # this is not really necessary
task = models.ForeignKey(UserTask,on_delete=models.CASCADE)
product_amount = models.ForeignKey(ProductAmount, on_delete=models.CASCADE)
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
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()
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.