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
Related
when i try to run a view I get this error:
AttributeError: Got AttributeError when attempting to get a value for field `inreplytouser` on serializer `ContentFeedPostCommentSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'inreplytouser'.
Here is my model:
class ContentFeedPostComments(models.Model):
inreplytouser = models.ForeignKey(SiteUsers, null=True, related_name='replytouser', blank=True, on_delete=models.CASCADE)
inreplytotopcomment = models.BigIntegerField(null=True, blank=True)
timecommented = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(SiteUsers, on_delete=models.CASCADE)
contentcreator = models.ForeignKey(ContentCreatorUsers, on_delete=models.CASCADE)
contentfeedpost = models.ForeignKey(ContentFeedPost, on_delete=models.CASCADE)
text = models.CharField(max_length=1000)
here is the serializer:
class ContentFeedPostCommentSerializer(ModelSerializer):
id = IntegerField()
inreplytouser = SiteusersSerializer()
user = SiteusersSerializer()
contentcreator = ContentCreatorSerializer()
class Meta:
model = ContentFeedPostComments
fields = ('id','inreplytouser', 'inreplytotopcomment', 'timecommented',
'user', 'contentcreator', 'contentfeedpost', 'text')
here is the view:
class ContentFeedPostsComments(APIView):
def get(self, request, *args, **kwargs):
postid = kwargs.get('postid')
contentfeedpost = get_object_or_404(ContentFeedPost, id=postid)
topcomments = ContentFeedPostComments.objects.filter(contentfeedpost= contentfeedpost, inreplytotopcomment= None).order_by('timecommented')
replycomments = ContentFeedPostComments.objects.filter( contentfeedpost = contentfeedpost, inreplytotopcomment__isnull = False).order_by('timecommented')
serializedtopcomments = ContentFeedPostCommentSerializer(topcomments)
serializedreplycomments = ContentFeedPostCommentSerializer(replycomments)
payload = {
'topcomments': serializedtopcomments.data,
'replycomments': serializedreplycomments.data
}
return Response(payload)
I was reading something about source being passsed into the inreplytouser field of the serializer field but that makes no sense. Your wisdom and knowledge on this situation is greatly appreciated.
Since querysets are a collection of objects, many=True..[DRF-doc] needs to be set in the serializer:
serializedtopcomments = ContentFeedPostCommentSerializer(topcomments, many=True)
serializedreplycomments = ContentFeedPostCommentSerializer(replycomments, many=True)
I am getting error Cannot assign "'1'": "dropdown.drp1" must be a "basedrop" instance. I am sharing my code. Kindly help. I got some solutions on stack but I did not understand how to implement that in my case. Django error. Cannot assign must be an instance
models.py
class basedrop(models.Model):
name = models.CharField(max_length=50,blank=False,null=False)
def __str__(self):
return self.name
class subdrop(models.Model):
name = models.CharField(max_length=100,blank=False,null=False)
bsdrop = models.ForeignKey(basedrop,null=False,blank=False,on_delete=models.CASCADE)
def __str__(self):
return self.name
class lastdrop(models.Model):
name = models.CharField(max_length=100,blank=False,null=False)
sbdrop = models.ForeignKey(subdrop,null=False,blank=False,on_delete=models.CASCADE)
def __str__(self):
return self.name
class dropdown(models.Model):
name = models.CharField(max_length=50)
drp1 = models.ForeignKey(basedrop,max_length=50,on_delete=models.CASCADE)
drp2 = models.ForeignKey(subdrop,max_length=50,on_delete=models.CASCADE)
drp3 = models.ForeignKey(lastdrop,max_length=50,on_delete=models.CASCADE)
def __str__(self):
return self.name
views.py
def create_drop(request):
if request.method == 'POST':
form = dropdownForm(request.POST or None)
if form.is_valid():
form = dropdown(name=request.POST.get('name'),drp1_Id=int(request.POST.get('drp1')),
drp2_Id=int(request.POST.get('drp2')),drp3_Id=int(request.POST.get('drp3')))
form.save()
return HttpResponse('<p>this is working</p>')
form = dropdownForm()
return render(request,'drop.html',{'form':form})
forms.py
class dropdownForm(forms.ModelForm):
drp1 = forms.ChoiceField(choices=((bs.get('id'),bs.get('name')) for bs in basedrop.objects.all().values('id','name')))
class Meta:
model = dropdown
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['drp2'].queryset = subdrop.objects.none()
self.fields['drp3'].queryset = lastdrop.objects.none()
if 'drp1' in self.data:
try:
country_id = int(self.data.get('drp1'))
self.fields['drp2'].queryset = subdrop.objects.filter(id=country_id).order_by('name')
except (ValueError, TypeError):
pass
elif 'drp2' in self.data:
try:
country_id = int(self.data.get('drp2'))
self.fields['drp3'].queryset = lastdrop.objects.filter(id=country_id).order_by('name')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['drp2'].queryset = self.instance.drp1.city_set.order_by('name')
self.fields['drp3'].queryset = self.instance.drp2.city_set.order_by('name')
I don't know if it could cause failures but you're passing the POSTed argument 'drp1' as integer for drp1_Id, drp2_Id and drp3_Id.
You'd have it much easier if you choose a more intuitive coding style.
For example this line:
form = dropdown(name=request.POST.get('name'), drp1_Id=int(request.POST.get('drp1')), drp2_Id=int(request.POST.get('drp1')), drp3_Id=int(request.POST.get('drp1')))
If you get the objects and pass them to the dropdown you gain readability specially if there is an error:
drp1_pk = request.POST.get('drp1')
drp1 = basedrop.objects.get(pk=drp1_pk)
drp2 = subdrop.objects.get(pk=drp1_pk)
drp3 = lastdrop.objects.get(pk=drp1_pk)
form = dropdown(name=request.POST.get('name'), drp1=drp1, drp2=drp2, drp3=drp3)
But again:
It looks strange to pass the same primary key to three different models.
Th models.py is:
from django.db import models
class Type(models.Model):
letter = models.CharField(max_length = 1)
name = models.CharField(max_length = 10)
class Code(models.Model):
type = models.ForeignKey(Type, related_name = 'code', blank = True, default = None)
the serializers.py is:
import collections
from rest_framework import serializers
from code.models import Type, Code
class TypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Type
fields = ('letter','name')
class TypeField(serializers.HyperlinkedModelSerializer):
class Meta:
model = Type
fields = ('letter',)
class CodeSerializer(serializers.HyperlinkedModelSerializer):
type = TypeField() #serializers.ChoiceField(???)
def create(self, validated_data):
c = Code()
c.type = Type.objects.get(letter = validated_data['type']['letter'])
c.save()
return c
class Meta:
model = Code
fields = ('type',)
the views.py is:
from rest_framework import viewsets
from code.models import Code, Type
from code.serializers import CodeSerializer, TypeSerializer
class CodeViewSet(viewsets.ModelViewSet):
queryset = Code.objects.all()
serializer_class = CodeSerializer
class TypeViewSet(viewsets.ModelViewSet):
queryset = Type.objects.all()
serializer_class = TypeSerializer
Is possible use a ChoiseField for choose the type inside the Code, When I create an istance of the Code object instead a CharField?
Possible Solution
I find a possible solution, I derived class ChoiseField to DynamicChoiceField
class DynamicChoiceField(serializers.ChoiceField):
def __init__(self, **kwargs):
super(DynamicChoiceField, self).__init__([],**kwargs)
def set_choices(self, choices):
pairs = [
isinstance(item, (list, tuple)) and len(item) == 2
for item in choices
]
if all(pairs):
self.choices = OrderedDict([(key, display_value) for key, display_value in choices])
else:
self.choices = OrderedDict([(item, item) for item in choices])
self.choice_strings_to_values = dict([
(six.text_type(key), key) for key in self.choices.keys()
])
and change the CodeSerializer in:
class CodeSerializer(serializers.HyperlinkedModelSerializer):
type = TypeField(read_only=True)
choise_of_type = DynamicChoiceField(allow_blank=False, write_only=True)
def __init__(self, *args, **kwargs):
types = Type.objects.all()
choices = [(t.letter,t.name) for t in types]
self.fields['choise_of_type'].set_choices(choices)
super(CodeSerializer, self).__init__(*args, **kwargs)
def create(self, validated_data):
c = Code()
c.type = Type.objects.get(letter = validated_data['choise_of_type'])
c.save()
return c
class Meta:
model = Code
fields = ('type', 'choise_of_type',)
the only flaw of this solution is that I need of two fields insted only one (type,choise_of_type)
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)
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.