Serialization in Django: How to serialize models with Foreign keys
Serialization is one of the main processes of creating APIs. We want to share data between different types of frontends (Web, Android, IOS) in a way that is easily accessible by them all. Serialization is the process of converting different data into a well-defined format and send it as the API response. I have been away from my blog because there was nothing really to discuss. I was constantly trying to do some stuff and was constantly failing. But, after a week-long struggle and some help I was able to get over this struggling period and now shifted to the next task in my task list. unit tests and then serializers.
Prerequisites 1: DjangoFirst things first, Why do we need serializers? We know that our data is present in the database. We also know that we cannot send that data easily to different formats through our database. So, we use the simple concept of serialization that converts the database data or any other data into JSON, XML or YAML format which can be easily transmitted over the network. Easy, right? Let’s dive in and see some code snippets. Here is the model file of my GSoC-2017 project.
class ScanInfo(models.Model): def __str__(self): return self.scan_type scan_types = ( ('URL', 'URL'), ('Local Scan', 'localscan'), ) scan_type = models.CharField(max_length=20, choices=scan_types, default='URL') is_complete = models.BooleanField() class UserInfo(models.Model): def __str__(self): return self.user.username user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) scan_info = models.ForeignKey(ScanInfo) class URLScanInfo(models.Model): def __str__(self): return self.URL scan_info = models.ForeignKey(ScanInfo) URL = models.URLField(max_length=2000) class LocalScanInfo(models.Model): def __str__(self): return self.folder_name scan_info = models.ForeignKey(ScanInfo) folder_name = models.CharField(max_length=200) class CodeInfo(models.Model): def __str__(self): return self.total_code_files scan_info = models.ForeignKey(ScanInfo) total_code_files = models.IntegerField(null=True, blank=True) code_size = models.IntegerField(null=True, blank=True, default=0)
class ScanInfoSerializer(serializers.ModelSerializer): class Meta: model = ScanInfo fields = '__all__' class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = UserInfo fields = '__all__' class URLScanInfoSerializer(serializers.ModelSerializer): class Meta: model = URLScanInfo fields = '__all__' class LocalScanInfoSerializer(serializers.ModelSerializer): class Meta: model = LocalScanInfo fields = '__all__' class CodeInfoSerializer(serializers.ModelSerializer): class Meta: model = CodeInfo fields = '__all__'
serializerand told it about the way in which it should go forward with the serializing.
class GodSerializer(serializers.Serializer): """ Another good serializer to handle all the serialization activities """ code_info = CodeInfoSerializer() url_scan = UrlScanInfoSerializer() local_scan = LocalScanInfoSerializer() scan_result = ScanResultSerializer() scan_file_info = ScanFileInfoSerializer(many=True) license = LicenseSerializer(many=True) matched_rule = MatchedRuleSerializer(many=True) matched_rule_license = MatchedRuleLicenseSerializer(many=True) copyright = CopyrightSerializer(many=True) copyright_holder = CopyrightHolderSerializer(many=True) copyright_statement = CopyrightStatementSerializer(many=True) copyright_author = CopyrightAuthorSerializer(many=True) package = PackageSerializer(many=True) scan_error = ScanErrorSerializer(many=True)
class GodSerializerHelper(object): def __init__(self, scan_info): self.scan_info = scan_info self.code_info = CodeInfo.objects.get(scan_info=scan_info) self.url_scan = URLScanInfo.objects.get(scan_info=scan_info) self.local_scan = None self.scan_result = ScanResult.objects.get(code_info=self.code_info) self.scan_file_info = ScanFileInfo.objects.filter(scan_result=self.scan_result) self.license = License.objects.filter(scan_file_info__in=(self.scan_file_info)) self.matched_rule = MatchedRule.objects.filter(license__in=(self.license)) self.matched_rule_license = MatchedRuleLicenses.objects.filter(matched_rule__in=(self.matched_rule)) self.copyright = Copyright.objects.filter(scan_file_info__in=(self.scan_file_info)) self.copyright_holder = CopyrightHolders.objects.filter(copyright__in=(self.copyright)) self.copyright_statement = CopyrightStatements.objects.filter(copyright__in=(self.copyright)) self.copyright_author = CopyrightAuthor.objects.filter(copyright__in=(self.copyright)) self.package = Package.objects.filter(scan_file_info__in=(self.scan_file_info)) self.scan_error = ScanError.objects.filter(scan_file_info__in=(self.scan_file_info))
__in, this is used to remove a big error of calling a model by using multiple rows of the
ForeignKey. Let me try it once more. We know when we use objects and
filterit, it returns more than one row. Now as the variable is storing more than one row, it cannot be passed to next
objects.filterbecause it has more than one row itself. After this, for testing, I used the following code to see if the things are looking well.
s = GodSerializerHelper(ScanInfo.objects.get(pk=51)) s = GodSerializer(s) s.data
Did you enjoy reading or think it can be improved? Don’t forget to leave your thoughts in the comments section below! If you liked this article, please share it with your friends, and read a few more!