I'm following the Django Rest documentation for writing nested serializer but it is giving me attribute error.
Here are my models:
class Objects(TimeStampModel):
projects = models.ForeignKey(Projects,related_name='proj_obj',on_delete=models.CASCADE)
object_name = models.CharField(max_length=50)
object_description = models.TextField()
object_main_table = models.CharField(max_length=50)
object_primary_key = models.CharField(max_length=50)
object_age_field = models.CharField(max_length=50)
date_format = models.CharField(max_length=50)
def __str__(self):
return self.object_name
class ObjectDefinition(TimeStampModel):
ATTRIBUTE = 'Attribute'
RELATION = 'Relation'
TYPE_CHOICES = (
(ATTRIBUTE, 'Attribute'),
(RELATION, 'Relation'),
)
obj = models.ForeignKey(Objects,related_name='obj_def',on_delete=models.CASCADE)
from_table = models.CharField(max_length=50)
from_table_field = models.CharField(max_length=50)
to_table = models.CharField(max_length=50)
to_table_field = models.CharField(max_length=50)
relation_type = models.CharField(max_length=50,choices=TYPE_CHOICES)
relation_sequence = models.CharField(max_length=50)
value_field = models.CharField(max_length=50, blank=True, null=True)
Here is my serializers.py snippet:
class ObjectDefinitionSerializer(serializers.ModelSerializer):
class Meta:
model = ObjectDefinition
fields = ('from_table','from_table_field','to_table','to_table_field','relation_type','value_field')
class ObjectSerializer(serializers.ModelSerializer):
definition = ObjectDefinitionSerializer(many=True)
object_description = serializers.CharField(required=False, allow_null=True, allow_blank=True
)
class Meta:
model = Objects
fields = ('projects','object_name','object_description','object_main_table','object_primary_key','object_age_field','date_format','definition')
def validate(self, data, *args, **kwargs):
date_format = data.get('date_format')
if date_format not in ['YYYYMMDD', 'DDMMYYYY']:
msg = ('Date format is incorrect')
raise serializers.ValidationError({'error_msg': msg})
return super(ObjectSerializer, self).validate(data, *args, **kwargs)
def create(self, validated_data):
definition_data = validated_data.pop('definition')
obj = Objects.objects.create(**validated_data)
for data in definition_data:
ObjectDefinition.objects.create(obj=obj, **data)
return obj
My views.py:
class CreateObject(CreateAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = ObjectSerializer
After hitting POST, objects.create works fine for both the models but at return obj, it throws me this error:
Exception Value:
Got AttributeError when attempting to get a value for field definition on serializer ObjectSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Objects instance.
Original exception text was: 'Objects' object has no attribute 'definition'.
What am I missing?
The ObjectDefinition.obj's related_name is obj_def which doesn't match your serializer.
You can fix that by providing the source argument:
definition = ObjectDefinitionSerializer(source='obj_def', many=True)
Related
I have been attempting to import data into my Django project using Django import-export. I have two models Ap and Job, Job has a FK relationship with Ap. Using the Admin, I can select the file and the type, CSV. So far my program seems to run, but gets hung up on the FK. I'm close, something is off and causing the import script to fail.
Models.py
class Ap(models.Model):
line_num = models.IntegerField()
vh = models.IntegerField()
vz = models.IntegerField()
status = models.CharField(
choices=statuses, default="select", max_length=40)
classified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Job(models.Model):
aplink = models.ForeignKey(Ap, related_name=(
"job2ap"), on_delete=models.CASCADE)
job_num = models.IntegerField()
description = models.CharField(max_length=200)
category = models.CharField(
choices=categories, default="select", max_length=40)
status = models.CharField(
choices=statuses, default="select", max_length=40)
dcma = models.BooleanField(default=False),
due_date = models.DateField(blank=True),
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class ImportView(View):
def get(self, request):
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
def post(self, request):
form = ImportForm(request.POST, request.FILES)
job_resource = JobResource()
data_set = Dataset()
if form.is_valid():
file = request.FILES['import_file']
imported_data = data_set.load(file.read())
result = job_resource.import_data(
data_set, dry_run=True) # Test the data import
if not result.has_errors():
job_resource.import_data(
data_set, dry_run=False) # Actually import now
else:
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
resource.py
class CharRequiredWidget(widgets.CharWidget):
def clean(self, value, row=None, *args, **kwargs):
val = super().clean(value)
if val:
return val
else:
raise ValueError('this field is required')
class ForeignkeyRequiredWidget(widgets.ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:
print(self.field, value)
return self.get_queryset(value, row, *args, **kwargs).get(**{self.field: value})
else:
raise ValueError(self.field + " required")
class JobResource(resources.ModelResource):
aplink = fields.Field(column_name='aplink', attribute='aplink', widget=ForeignkeyRequiredWidget(Ap,'id'),
saves_null_values=False)
job_num = fields.Field(saves_null_values=False, column_name='job_num', attribute='job_num',
widget=widgets.IntegerWidget())
description = fields.Field(column_name='description', attribute='description', saves_null_values=False,
widget=CharRequiredWidget())
class Meta:
model = Job
fields = ('aplink', 'job_num', 'description',)
clean_model_instances=True
admin.py
class JobResource(resources.ModelResource):
class Meta:
model=Job
fields=('aplink','job_num','description',)
class JobAdmin(ImportExportModelAdmin):
resource_class = JobResource
admin.site.register(Job, JobAdmin)
CSV file, data to import. I have tried leaving the first column empty, as will as putting the Id of the only Ap stored in the table ie 1. I have also tried hard coding the line_num, which is 1200 the first column as well.
CSV file for importing data:
Date importing errors:
In your resources, while defining fields, you need to include id field in the list. So change JobResource to the following:
class JobResource(resources.ModelResource):
class Meta:
model = Job
fields = ('id', 'aplink', 'job_num', 'description')
If you have defined a custom id field, then you will need to provide:
import_id_fields = ('your_id_field')
I have a webapp where we can create communities with django as a backend, but when i try to send a POST to create a community, I get:
community_configuration: ["Incorrect type. Expected pk value, received str."]
My POST:
{
title: knkn kn .k jbjnmn,
logo: [object File],
is_active: true,
description: test,
welcome_message: Welcome message,
org_id: 114,
community_configuration: About us,Community news,FAQs,Supporters,Resources,
}
Here are my serializers:
class MicroConfigurationSerializer(serializers.ModelSerializer):
class Meta:
model = MicroConfiguration
fields = [
'name',
]
def to_representation(self, instance):
return instance.name
class CommunityConfigurationSerializer(serializers.ModelSerializer):
micro_configurations = MicroConfigurationSerializer(many=True, read_only=True)
class Meta:
model = CommunityConfiguration
fields = '__all__'
class CommunitySerializer(serializers.ModelSerializer):
logo = serializers.SerializerMethodField()
organisation = serializers.SerializerMethodField()
community_configuration = CommunityConfigurationSerializer()
class Meta:
model = Community
fields = (
...
etcetc
...
)
Heres my model:
class MicroConfiguration(core_models.BaseModel):
name = models.CharField(max_length=32, unique=True)
permissions = models.ManyToManyField(Permission)
def __str__(self):
return self.name
class CommunityConfiguration(core_models.BaseModel):
name = models.CharField(max_length=32)
micro_configurations = models.ManyToManyField(MicroConfiguration)
permission_group = models.ForeignKey(
Group,
on_delete=models.SET_NULL,
null=True
)
def __str__(self):
return self.name
class Community(core_models.BaseModel):
accounts = models.ManyToManyField(
settings.AUTH_USER_MODEL,
through='associates.AccountCommunity',
through_fields=('community', 'account')
)
goals = models.ManyToManyField(Goal, through='associates.CommunityGoal')
type = models.ManyToManyField(CommunityType, through='CommunityAttribute')
communities = models.ManyToManyField(
'self',
through='associates.CommunityCommunity',
symmetrical=False,
)
crossposts = models.ManyToManyField(
Action,
through='activities.CommunityCrosspost',
through_fields=('community', 'action'),
related_name='communities',
)
title = models.CharField(max_length=64)
logo = models.ImageField(null=True, blank=True)
intro_media = models.ForeignKey(
'associates.MediaStore',
null=True,
on_delete=models.SET_NULL
)
website_url = models.CharField(max_length=256, blank=True)
is_invite_only = models.BooleanField(default=False)
description = models.TextField(blank=True)
intro_message = models.TextField(blank=True)
welcome_message = models.TextField(blank=True)
signup_autojoin = models.BooleanField(default=False)
community_configuration = models.ForeignKey(
CommunityConfiguration,
default=1,
null=True,
on_delete=models.SET_NULL,
help_text='Do not edit directly, create a new custom config instead, \
as it is reference by difference community.',
)
objects = models.Manager()
associates = AssociationManager()
#property
def url(self):
return reverse(
'communities:detail',
kwargs={
'id': self.id,
}
)
class Meta:
verbose_name = 'Community'
verbose_name_plural = 'Communities'
def __str__(self):
return self.title
Here's my views:
class CommunityCreateAPIView(CreateAPIView):
serializer_class = CommunityCreateSerializer
def create(self, request, **kwargs):
organisation_id = request.data.get('org_id')
micro_configurations_qd = request.data.copy()
micro_configurations = micro_configurations_qd.pop('community_configuration', False)
if micro_configurations:
micro_configurations = micro_configurations[0].split(',')
user = request.user
data = {}
permitted = AccountOrganisation.objects.filter(
account=user,
organisation_id=organisation_id,
is_active=True,
association__permissions__codename='add_community',
).exists()
if not permitted and not request.user.is_superuser:
data['message'] = 'Action not allowed'
return Response(
data=data,
status=status.HTTP_400_BAD_REQUEST,
)
response = super().create(request, **kwargs)
if response.status_code == 400:
data['message'] = 'Failed to update community'
return Response(
data=data,
status=status.HTTP_400_BAD_REQUEST,
)
community_id = response.data.get('id')
OrganisationCommunity.objects.create(
community_id=community_id,
organisation_id=organisation_id,
)
association = Group.objects.get(name='Community Admin')
AccountCommunity.objects.create(
community_id=community_id,
account=user,
association=association,
)
if micro_configurations:
com_config_qs = CommunityConfiguration.objects.filter(
micro_configurations__name__in=micro_configurations
).annotate(
num_micro_config=Count('micro_configurations__name')
).filter(
num_micro_config=len(micro_configurations)
)
community_configuration = None
if micro_configurations:
for com_config in com_config_qs:
micro_config_count = com_config.micro_configurations.count()
if micro_config_count == len(micro_configurations):
community_configuration = com_config
break
if community_configuration:
Community.objects.filter(
pk=community_id
).update(
community_configuration=community_configuration
)
elif micro_configurations:
micro_qs = MicroConfiguration.objects.filter(
name__in=micro_configurations
)
community_config = CommunityConfiguration.objects.create(
name='Custom'
)
community_config.micro_configurations.set(micro_qs)
community_config.save()
Community.objects.filter(
pk=community_id
).update(
community_configuration=community_config
)
return response
I've been stuck with this for hours, any help?
Your serializer expects a pk (or id) of the community_configuration instance.
basically you have it set up so that you need to create a community_configuration entry first, then fetch the id of the new created entry and use that id when creating your community.
If you want to have the community_configuration instance to be created while creating the community then an option to do that would be to override the create method in the serializer and extract the community_configuration data then create a new entry for that model and use it, something like this
class CommunitySerializer(serializers.ModelSerializer):
# .. put your serializer code here
def create(self, validated_data):
community_configuration= validated_data.pop('community_configuration')
config_instance, created = CommunityConfiguration.objects.get_or_create(<community_configuration>) # modify this however you need to create the CommunityConfiguration instance
community_instance = community.objects.create(**validated_data, community_configuration=config_instance)
return community_instance
you will need to probably modify the code for it to follow your needs.
I didn't read all of your models, not sure how you are gonna create the nested models from the input value but you get the point.
This is my models.py
class PdfParsed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE)
pdf_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200, blank=True, null=True)
status = models.IntegerField(blank=True, null=True)
this is my views.py
class pdfListView(LoginRequiredMixin,ListView):
model = PdfParsed.objects.filter(user_id=6)
login_url = '/login/'
context_object_name = 'pdfparsed_list'
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
aa=os.listdir('media/pdfs')
data['pdf_parsed'] = sum('.pdf' in s for s in aa)
return data
what i wanted to achieve from above code is to print pdfs that are uploaded with respect to user here for testing purpose i used number 6 but its not working it is showing this error:
File "/home/ideas/anaconda3/lib/python3.8/site-packages/django/views/generic/list.py", line 33, in get_queryset
queryset = self.model._default_manager.all()
AttributeError: 'QuerySet' object has no attribute '_default_manager'
add this in your code:
def get_queryset(self):
return super().get_queryset().filter(user_id=self.request.user.id)
and keep model = PdfParsed
Ive been trying to create an API that would return all objects from Like model however, I received an error (Expected view likeList to be called with a URL keyword argument named "id". Fix your URL conf, or set the .lookup_field attribute on the view correctly.).
Here is my model
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
objects = models.Manager()
image = models.ImageField(upload_to='post_pics')
def __str__(self):
return self.title
#property
def useremail(self):
return self.author.email
#property
def owner(self):
return self.author
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk':self.pk})
def get_api_url(self, request=None):
return api_reverse('post-detail', kwargs={'pk': self.pk}, request=request)
def get_like_count(self):
return self.like_set.count()
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
Serializer
class likeserializers(serializers.ModelSerializer):
username = serializers.SerializerMethodField(read_only=True)
post_title = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Like
fields = ('id','created',
'user','username',
'post','post_title')
def get_username(self, obj):
return obj.user.username
def get_post_title(self, obj):
return obj.post.title
Views
class likeList(generics.RetrieveUpdateDestroyAPIView):
lookup_field = 'id'
serializer_class = likeserializers
def get_queryset(self):
return Like.objects.all()
URLS
urlpatterns = [
path('users/', API_views.userList.as_view(), name = 'users'),
path('users/id=<int:id>/', API_views.userListbyID.as_view(), name = 'usersid'),
path('posts/', API_views.postList.as_view(), name = 'post'),
path('posts/id=<int:id>', API_views.postListbyID.as_view(), name = 'postid'),
path('likes/', API_views.likeList.as_view(), name = 'likes'),
path('likes/id=<int:id>', API_views.likeListbyID.as_view(), name = 'likesid'),
path('follows/', API_views.followList.as_view(), name = 'likes'),
path('follows/id=<int:id>', API_views.followListbyID.as_view(), name = 'likesid'),
]
As per the error I should either fix my URL conf, or set the .lookup_field attribute on the view correctly. It works as intended if I change my URL conf however, if I use only lookup_field it fix the issue. I have completely the same view for posts and it works.
Post serializer:
class postserializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField(read_only=True)
like_count = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Post
fields = ('url','id',
'title','content',
'date_posted','author',
'useremail','like_count')
def get_url(self,obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
def get_like_count(self,obj):
return obj.get_like_count()
def validate_title(self,value):
qs = Post.objects.filter(title__iexact = value)
#exclude the same instance
if self.instance:
qs = qs.exclude(pk=self.instance.pk)
#if title already exists raise error
if qs.exists():
raise serializers.ValidationError(f"Post with title '{value}' already exists")
return value
and post view:
class postList(mixins.CreateModelMixin, generics.ListAPIView):
lookup_field = 'id'
serializer_class = postserializer
permission_classes = [IsOwnerOrReadOnly]
# permissions can be set up here as well + in settings.py
# permission_classes =
def get_queryset(self):
qs = Post.objects.all()
query = self.request.GET.get("q")
if query is not None:
qs = qs.filter(
Q(title__icontains = query)|
Q(content__icontains = query)
).distinct()
return qs
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
What am I missing? What is the difference between my post and like views/serializers which are causing this issue?
I have a linked model:
class Children(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
child_name = models.CharField(max_length=150, null=True, blank=True)
slug = AutoSlugField(populate_from='child_name')
blood_group = models.CharField(max_length=5, blank=True)
class Meta:
unique_together = ('slug', 'person')
def get_absolute_url(self):
return self.person.get_absolute_url()
def get_delete_url(self):
return reverse(
'member:children-delete',
kwargs={
'person_slug': self.person.slug,
'children_slug': self.slug})
def get_update_url(self):
return reverse(
'member:children-update',
kwargs={
'person_slug': self.person.slug,
'children_slug': self.slug})
my forms.py:
class ChildrenForm( SlugCleanMixin, forms.ModelForm):
class Meta:
model = Children
exclude = ('person',)
def clean(self):
cleaned_data = super().clean()
slug = cleaned_data.get('slug')
person_obj = self.data.get('person')
exists = (
Children.objects.filter(
slug__iexact=slug,
person=person_obj,
).exists())
if exists:
raise ValidationError(
"Children with this Slug "
"and Person already exists.")
else:
return cleaned_data
def save(self, **kwargs):
instance = super().save(commit=False)
instance.person = (
self.data.get('person'))
instance.save()
self.save_m2m()
return instance
views.py:
class ChildrenCreate( ChildrenFormMixin, ChildrenGetObjectMixin,
PersonContextMixin,CreateView):
template_name = 'member/children_form.html'
model = Children
form_class = ChildrenForm
class ChildrenUpdate(ChildrenFormMixin, ChildrenGetObjectMixin,
PersonContextMixin,UpdateView):
template_name = 'member/children_form.html'
model = Children
form_class = ChildrenForm
slug_url_kwarg = 'children_slug'
class ChildrenDelete(ChildrenFormMixin,ChildrenGetObjectMixin,
PersonContextMixin,DeleteView):
model = Children
slug_url_kwarg = 'children_slug'
def get_success_url(self):
return (self.object.person
.get_absolute_url())
my utils.py:
class ChildrenFormMixin():
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
if self.request.method in ('POST', 'PUT'):
self.person = get_object_or_404(
Person,
slug__iexact=self.kwargs.get(
self.person_slug_url_kwarg))
data = kwargs['data'].copy()
data.update({'person': self.person})
kwargs['data'] = data
return kwargs
class ChildrenGetObjectMixin():
def get_object(self, queryset=None):
person_slug = self.kwargs.get(
self.person_slug_url_kwarg)
children_slug = self.kwargs.get(
self.slug_url_kwarg)
return get_object_or_404(
Children,
slug__iexact=children_slug,
person__slug__iexact=person_slug)
class PersonContextMixin():
person_slug_url_kwarg = 'person_slug'
person_context_object_name = 'person'
def get_context_data(self, **kwargs):
person_slug = self.kwargs.get(
self.person_slug_url_kwarg)
person = get_object_or_404(
Person, slug__iexact=person_slug)
context = {
self.person_context_object_name:
person,
}
context.update(kwargs)
return super().get_context_data(**context)
The children created more than one for same name of same parents. When I tried to edit children it gives "get() returned more than one Children -- it returned 2!" error. In traceback, it said, 'person__slug__iexact=person_slug' is the direct causes of this traceback.
In the form, I added clean method to catch the error and maintain uniqueness of children name of same parents but it not worked. Could I get suggestions where I do wrong?
Edit:
my Person model:
class Person(models.Model):
name = models.CharField(max_length=250)
slug = AutoSlugField(populate_from='name')
birth_date = models.DateField(null=True, blank=True)
blood_group = models.CharField(max_length=5)
present_address = models.CharField(max_length=250, blank=True)
permanent_address = models.CharField(max_length=250, blank=True)
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name='member_persons')
class Meta:
ordering = ['name']
unique_together = ['name', 'birth_date']
I believe you are using AutoSlugField from django-autoslug, and you trying to get by non-unique field. AutoSlugField won't make your field unique by default, from docs:
AutoSlugField can also perform the following tasks on save:
populate itself from another field (using populate_from),
use custom slugify function (using slugify or Settings), and
preserve uniqueness of the value (using unique or unique_with).
None of the tasks is mandatory, i.e. you can have auto-populated non-unique fields, manually entered unique ones (absolutely unique or within a given date) or both.
So quick fix would be slug = AutoSlugField(populate_from='child_name', unique=True)
UPDATE(Since you posted your Person model)
The problem is the same and solution is the same.
Explanation:
For example you have two Person objects:
id name slug birth_date
1 alex alex 10.10.2016
2 alex alex 10.10.2015
This won't violate unique_together = ['name', 'birth_date']
And you got two Children objects:
id name slug person_id
1 john john 1
2 john john 2
And that won't violate unique_together = ('slug', 'person') neither
Then you are making query
get_object_or_404(
Children,
slug__iexact='john',
person__slug__iexact='alex')
Which would match two objects. So you got problem. Quick fix would be to make slug unique=True.