serializer showing name of model - django

I want to serialize data from nested queryset:
I have working code but output from serializer showing too many data. I want hide this for security reason.
example output:
(...)
"gallery": "[{"model": "mainapp.imagesforgallery", "pk": 1, "fields": {"user": 1, "image": "uploads/2022/8/6/drw/Adapta-KDE-theme_JOgL4kO.webp", "thumbnail": ""}}]"
(...)
this is models.py
class ImagesForGallery(models.Model):
user = models.ForeignKey(UserProfile, null=True, blank=True, on_delete=models.CASCADE)
image = models.ImageField(upload_to=user_directory_path, blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
def __str__(self):
return 'User: {} || Image: {}'.format(self.user, self.image)
class Gallery(models.Model):
project = models.ForeignKey(Projects, null=True, blank=True, on_delete=models.CASCADE)
project_gallery = models.ManyToManyField(ImagesForGallery, blank=True, related_name='project_gallery')
def __str__(self):
return '{}'.format(self.project)
This is my view
class HomeView(viewsets.ModelViewSet):
serializer_class = ProjSerializer
queryset = Proj.objects.all()
def list(self, request, *args, **kwargs):
response = super(HomeView, self).list(request, args, kwargs)
gal = Gallery.objects.all()
for d in response.data:
for g in gal:
if d['uuid'] == str(g.project.uuid):
qs = g.project_gallery.get_queryset()
serialized_obj = serializers.serialize('json', qs)
d['gallery'] = serialized_obj
return response
This code compares the project model to the photo gallery model. If uuid is correct, include this gallery in the project and send json.
I'm not sure the code is efficient and safe. The question is how to modify the code so that it does not show the model name.

You need to use your ProjSerializer to serialize your queryset
if d['uuid'] == str(g.project.uuid):
qs = g.project_gallery.get_queryset()
serialized_obj = ProjSerializer(qs, many=True).data
d['gallery'] = serialized_obj

Related

Foreign Key Constraint Failure - Django. Manual assignment of Foreign Key to Model via POST Request

I'm working on a timesheet based system currently. I am getting a Foreign Key constraint failure when I am trying to assign the foreign key of one model to the other one.
Here are the two models
class Timesheet(models.Model):
id = models.CharField(primary_key=True, max_length=50, blank=True, unique=True, default=uuid.uuid4)
First_Name = models.CharField(max_length=32)
Last_Name = models.CharField(max_length=32)
Date_Created = models.DateField(auto_now_add=True)
Creator = models.ForeignKey(User, on_delete=models.DO_NOTHING)
Approved = models.BooleanField(default=False)
Company_Relationship = (
('Supplier', 'Supplier'),
('Contractor', 'Contractor'),
('Employee', 'Employee')
)
Worker_Type = models.CharField(max_length=32, choices=Company_Relationship)
Total_Days_Worked = models.DecimalField(decimal_places=2, max_digits=3)
class Meta:
ordering = ['-id']
#unique_together = ['Creator', 'Work_Week']
def get_absolute_url(self):
return reverse('timesheet-detail', kwargs={'pk': self.pk})
class InternalSheet(models.Model):
id = models.CharField(primary_key=True, max_length=50, blank=True, unique=True, default=uuid.uuid4)
Timesheet_id = models.ForeignKey(Timesheet, on_delete=models.DO_NOTHING)
Working_For = (
('7', 'Seven'),
('i', 'intelligent'),
('S', 'Sata'),
)
iPSL = (
('R16.1', 'Release 16.1'),
('R16', 'Release 16')
)
Company_name = models.CharField(max_length=5, choices=Working_For)
Company_name_change = models.CharField(max_length=5, choices=Working_For)
Internal_Company_Role = models.CharField(max_length=10, choices=iPSL)
DaysWorked = models.DecimalField(decimal_places=2, max_digits=3)
Managers = (
('GW', 'Greg Wood'),
('JC', 'Jamie Carson')
)
ManagerName = models.CharField(max_length=8, choices=Managers)
Approved = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('sheet-detail', kwargs={'pk': self.pk})
My issue is that I am getting a foreign key failure using this post request.
class TimesheetCreateView(LoginRequiredMixin, CreateView):
"""
Creates view and send the POST request of the submission to the backend.
"""
def get(self, request, *args, **kwargs):
internal_form_loop = create_internal_form_for_context()
context = {'form': CreateTimesheetForm(), 'UserGroup': User()}
context.update(internal_form_loop)
print("new", context)
return render(request, 'create.html', context)
def post(self, request, *args, **kwargs):
form = CreateTimesheetForm(request.POST)
internal_form_1 = CreateInternalSheetForm(request.POST)
if form.is_valid():
print("forms valid")
external_timesheet = form.save(commit=False)
print("self", self.request.user)
print("id", Timesheet.id)
external_timesheet.Creator = self.request.user
external_timesheet.save()
else:
print("Error Here")
if internal_form_1.is_valid():
print("Internal form valid")
internal = internal_form_1.save(commit=False)
internal.Timesheet_id_id = Timesheet.id
internal.id = uuid.uuid4()
internal.save()
return HttpResponseRedirect(reverse_lazy('timesheet-detail', args=[Timesheet.id]))
return render(request, 'create.html', {'form': form, 'UserGroup': User()})
It is failing on the line internal.save(). If I print the line internal.Timesheet_id_id I get a value like this, <django.db.models.query_utils.DeferredAttribute object at 0x000001580FDB75E0>. I'm guessing this is the issue? I need the actual Foreign key and not the location of that object. How do I do this. Thanks.
Figured out the issue, I had to replace the lines
internal.Timesheet_id_id = Timesheet.id
internal.id = uuid.uuid4()
internal.save()
with
internal.Timesheet_id_id = Timesheet.objects.get(id=external_timesheet.id)
internal.save()

Django multi language model / filter post by languages

There is my simple blog model;
class Article(models.Model):
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title_en = models.CharField(max_length = 120, verbose_name="Title_En")
title_de = models.CharField(max_length = 120, verbose_name="Title_De")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content_en = RichTextField(verbose_name="Content_En")
content_de = RichTextField(verbose_name="Content_De")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
def __str__(self):
return self.title
I use url's with language like this;
domainname.com/en/
domainname.com/de/
For example, how can I show only the contents that belong to title_de and content_de in the domainname.com/de urls?
How can I do filtering with language? Is there an easy solution to this?
(I usage django 2.1.2. i try django-modeltranslation or others dont work this django version...)
Thanks...
You can create a descriptor class that wraps the translated fields e.g.,
from django.utils import translation
class TranslatedField:
def __init__(self, field_name):
self.partial_field_name = field_name
def __get__(self, obj, objtype):
return getattr(obj, self.field_name)
def __set__(self, obj, value):
return setattr(obj, self.field_name, value)
#property
def field_name(self):
language_code = translation.get_language()
rerurn self.partial_field_name + '_' + language_code
class Article(models.Model):
title_en = models.CharField(max_length=120)
title_de = models.CharField(max_length=120)
title = Translated Field('title')
Then you can do
article = Article.objects.create(
title_en='In english',
title_de='In German'
)
print(article.title) # 'In english'
translation.set_language('de')
print(article.title) # 'In German'
article.title = 'In German!'
print(article.title) # 'In German!'
translation.set_language('en')
print(article.title) # 'In english'
(Untested, so there may be typos)
I would use something out of the box like https://github.com/deschler/django-modeltranslation
Filtering based on keyward argument is one of the option for this problem.I would prefer to add a language field 'EN' or 'DE' rather than repeating same kind of title and content field and filtering based on that. For example,
Article Model can be like
class Article(models.Model):
LANGUAGE_TYPES = (
('EN', 'EN'),
('DE', 'DE'),
)
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title = models.CharField(max_length = 120, verbose_name="Title")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content = RichTextField(verbose_name="Content")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
language = models.CharField(
max_length=10, choices=LANGUAGE_TYPES)
def __str__(self):
return self.title
Our urls can be like
from django.urls import path
from .views import (ArticleView)
urlpatterns = [
path('article/<slug:type>/', ArticleView.as_view(), name='article'),
]
And Our view can be like
from rest_framework import views, status
from .serializers import ArticleSerializer
from .models import Article
class ArticleView(views.APIView):
def get(self, request):
article_language_type = self.kwargs.get('type', None)
articles = Article.objects.filter(language=article_language_type)
serializer = ArticleSerializer(articles, many=True)
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

What should my django serializer def Update look like?

I'm trying to add content for the first time using a DRF back-end written by someone else. I am receiving this error...
django_1 | AssertionError: The `.update()` method does not support writable nested fields by default.
django_1 | Write an explicit `.update()` method for serializer `myapp.tracker.serializers.MedicationSerializer`, or set `read_only=True` on nested serializer fields.
How can I write the Update method?
I've done rails before so I'm familiar with the concepts? So when I see a method "create" in my serilaizers.py I think "there must be a way to write a def Update here" But since I'm new to django I have NO IDEA what that method should actually look like ^_^. This is where I'm stuck.
Here is the serializers.py , models.py, and views.py code specific to the model I am trying to update...let me know if I need to post another files contents.
What should my def update method look like?
my serializer.py
class MedicationSerializer(serializers.HyperlinkedModelSerializer):
cat = CatSerializer()
class Meta:
model = Medication
fields = (
'id',
'cat',
'name',
'duration',
'frequency',
'dosage_unit',
'dosage',
'notes',
'created',
'modified',
'showRow',
)
def create(self, validated_data):
cat_data = validated_data.pop('cat')
cat_obj = Cat.objects.get(**cat_data)
medication = Medication.objects.create(cat=cat_obj, **validated_data)
return medication
the models.py looks like this
class Medication(models.Model):
cat = models.ForeignKey(Cat, blank=True, null=True)
name = models.CharField(max_length=100)
duration = models.TextField(blank=True, null=True)
frequency = models.CharField(max_length=2)
dosage_unit = models.CharField(max_length=2, default=Weight.MILLILITERS)
dosage = models.IntegerField(blank=True, null=True)
notes = models.CharField(max_length=2048, blank=True, null=True)
created = models.DateTimeField(blank=True, null=True)
modified = models.DateTimeField(blank=True, null=True)
showRow = models.BooleanField(default=True)
def save(self, *args, **kwargs):
# Save time Medication object modified and created times
self.modified = datetime.datetime.now()
if not self.created:
self.created = datetime.datetime.now()
super(Medication, self).save(*args, **kwargs)
def __str__(self):
if self.cat:
cat_name = self.cat.name
else:
cat_name = "NO CAT NAME"
return "{cat}: {timestamp}".format(cat=self.cat.name, timestamp=self.created)
and the views.py ....
class MedicationViewSet(viewsets.ModelViewSet):
queryset = Medication.objects.all()
serializer_class = MedicationSerializer
filter_fields = ('cat__slug', 'cat__name')
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
Thank you for your time.
Try adding read-only=True to your Catserializer
class MedicationSerializer(serializers.HyperlinkedModelSerializer):
cat = CatSerializer(read_only=True)
class Meta:
model = Medication
fields = (
'id',
'cat',
'name',
'duration',
'frequency',
'dosage_unit',
'dosage',
'notes',
'created',
'modified',
'showRow',
)
def create(self, validated_data):
cat_data = validated_data.pop('cat')
cat_obj = Cat.objects.get(**cat_data)
medication = Medication.objects.create(cat=cat_obj, **validated_data)
return medication

Update M2M relationship django rest framework (many=true)

I try to update a channel:
PUT
content [{'url': 'http://localhost:8000/api/movies/2', 'title': u'Ariel', 'backdrop_path': u'/z2QUexmccqrvw1kDMw3R8TxAh5E.jpg', 'popularity': 0.082, 'poster_path': u'/8ld3BEg8gnynRsfj2AzbLocD8NR.jpg', 'release_date': datetime.date(1988, 10, 21), 'runtime': 69L, 'tagline': u'', 'vote_average': 9.0, 'vote_count': 0L}]
csrfmiddlewaretoken XXXXXXXXXXXXXXXXXXXXXXXXXXx
name cody private
owner http://localhost:8000/api/users/1
private 1
And I get this error:
instance should be a queryset or other iterable with many=True
And here is the code you need to understand what's going on.
class Channel(models.Model):
"""
A channel is a "container" for a users movies and television shows.
"""
PUBLIC_VISIBILITY, PRIVATE_VISIBILITY = 0, 1
VISIBILITY_CHOICES = (
(PUBLIC_VISIBILITY, 'public'),
(PRIVATE_VISIBILITY, 'private'),
)
owner = models.ForeignKey(User, related_name='owned_channels')
name = models.CharField(max_length=60)
content = models.ManyToManyField(Movie, db_table='channel_contents',
related_name='channels', null=True, blank=True, default=None)
subscribers = models.ManyToManyField(User, db_table='channel_subscribers',
related_name='subscribed_channels', null=True, blank=True, default=None)
created = models.DateTimeField(auto_now_add=True)
last_mod = models.DateTimeField(auto_now=True)
query = models.CharField(max_length=255, default='')
private = models.IntegerField(choices=VISIBILITY_CHOICES, default=PRIVATE_VISIBILITY)
default = models.BooleanField(default=False)
class Movie(models.Model):
id = models.BigIntegerField(primary_key=True)
adult = models.BooleanField()
backdrop_path = models.ImageField(upload_to='backdrop/')
budget = models.IntegerField(blank=True, null=True)
genres = models.ManyToManyField('Genre',
through='MovieGenre',
blank=True, null=True)
homepage = models.URLField(blank=True, null=True)
imdb_id = models.CharField(max_length=20, blank=True, null=True)
original_title = models.CharField(max_length=100)
overview = models.TextField(blank=True, null=True)
popularity = models.FloatField(blank=True, null=True)
poster_path = models.ImageField(upload_to='poster/')
release_date = models.DateField(blank=True, null=True)
revenue = models.IntegerField(blank=True, null=True)
runtime = models.IntegerField(blank=True, null=True)
tagline = models.CharField(max_length=200, blank=True, null=True)
title = models.CharField(max_length=100, db_index=True)
vote_average = models.FloatField(blank=True, null=True)
vote_count = models.IntegerField(blank=True, null=True)
actors = models.ManyToManyField('Actor',
through='MovieActor',
blank=True, null=True)
directors = models.ManyToManyField('Director',
through='MovieDirector',
blank=True, null=True)
production_companies = models.ManyToManyField(
'ProductionCompany',
through='MovieProduction',
blank=True, null=True)
Channel serializing code:
# Routes
url(r'^channels$', ChannelList.as_view(), name='channel-list'),
url(r'^channels/(?P<pk>\d+)$', ChannelDetail.as_view(), name='channel-detail'),
# Views
class ChannelList(generics.ListCreateAPIView):
"""
API endpoint that represents a list of users.
"""
model = Channel
serializer_class = ChannelSerializer
class ChannelDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint that represents a single users.
"""
model = Channel
serializer_class = ChannelSerializer
# Serializer
class ChannelSerializer(serializers.HyperlinkedModelSerializer):
content = MovieSerializer(many=True)
class Meta:
model = Channel
fields = ('url', 'owner', 'name', 'content', 'private')
As you can read here, nested relations currently don't support write operations. Use HyperlinkedRelatedField instead or write a custom serializer, that implements the features you need.
If you want to update the nested relation you can do like this,
class SchoolSerializer(serializers.HyperlinkedModelSerializer):
students = StudentSerializer(many=True, read_only=True)
students_ids = serializers.PrimaryKeyRelatedField(many=True,\
read_only=False, queryset=Student.objects.all(),\
source='students')
class Meta:
model = School
fields = ('name', 'image', 'address', 'url',\
'students', 'students_ids')
use PrimaryKeyRelatedField this will allow you to create, update, nested relations (Many-to-Many field) by just passing a list of id's
students will give you nested data,
students_ids can be used for write operations
This is a little outdated, but for future people looking for a potential solution to this problem, I found it useful to patch viewset.
You cannot read post params twice, which is the only thing preventing one from passing a Primary key for the related update and performing the m2m update in post_save
I made a custom viewset based on ModelViewSet with updated create and update statements:
In your app, you can create a module called viewsets.py:
# -*- coding: utf-8 -*-
from rest_framework import mixins
from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
class RelatedCreateModelMixin(mixins.CreateModelMixin):
'''
Monkey patch the UpdateModel for ModelViewSet Mixin to support data
transferrance from pre - to - save - to - post
'''
def create(self, request, *args, **kwargs):
data = request.DATA
serializer = self.get_serializer(data=data, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object, data=data)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True, data=data)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RelatedUpdateModelMixin(mixins.UpdateModelMixin):
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
self.object = self.get_object_or_none()
data = request.DATA
serializer = self.get_serializer(self.object, data=data,
files=request.FILES, partial=partial)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
try:
self.pre_save(serializer.object, data=data)
except ValidationError as err:
# full_clean on model instance may be called in pre_save,
# so we have to handle eventual errors.
return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)
if self.object is None:
self.object = serializer.save(force_insert=True)
self.post_save(self.object, data=data, created=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
self.object = serializer.save(force_update=True)
self.post_save(self.object, created=False)
return Response(serializer.data, status=status.HTTP_200_OK)
class RelatedModelViewSet(RelatedCreateModelMixin,
mixins.RetrieveModelMixin,
RelatedUpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
Then, in your view, use instead:
from MYAPP import viewsets
Which allows you to do something along the lines of:
def post_save(self, obj, *args, **kwargs):
data = kwargs.get('data')
model_id = data.get('id')
parent_obj = Model.objects.get(id=model_id)
method = self.request.method
if method == 'POST':
parent_obj.m2m.add(obj)
elif method == 'PUT':
parent_obj.m2m.remove(obj)
Not the most elegant solution, but I find it preferable to writing a custom serializer

django-piston: how to get values of a many to many field?

I have a model with ManyToManyField to another model. I would like to get all the info on a particular record (including the related info from other models) return by JSON.
How to get django-piston to display those values? I would be happy with just primary keys.
Or can you suggest another option ?
I may be wrong, but this should do it:
class PersonHandler(BaseHandler):
model = Person
fields = ('id', ('friends', ('id', 'name')), 'name')
def read(self, request):
return Person.objects.filter(...)
You need to define a classmethod on the handler that returns the many-to-many data, I don't believe Piston does this automatically.
class MyHandler(BaseHandler):
model = MyModel
fields = ('myfield', 'mymanytomanyfield')
#classmethod
def mymanytomanyfield(cls, myinstance):
return myinstance.mymanytomanyfield.all()
My code:
Models:
class Tag(models.Model):
"""docstring for Tags"""
tag_name = models.CharField(max_length=20, blank=True)
create_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.tag_name
class Author(models.Model):
"""docstring for Author"""
name = models.CharField(max_length=30)
email = models.EmailField(blank=True)
website = models.URLField(blank=True)
def __unicode__(self):
return u'%s' % (self.name)
class Blog(models.Model):
"""docstring for Blogs"""
caption = models.CharField(max_length=50)
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag, blank=True)
content = models.TextField()
publish_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s %s' % (self.caption, self.author, self.publish_time)
Handle:
class BlogAndTagsHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blog
fields = ('id' 'caption', 'author',('tags',('id', 'tag_name')), 'content', 'publish_time', 'update_time')
def read(self, request, _id=None):
"""
Returns a single post if `blogpost_id` is given,
otherwise a subset.
"""
base = Blog.objects
if _id:
return base.get(id=_id)
else:
return base.all() # Or base.filter(...)
Works petty good.