Iterating over model constraints in bulk data create on Django - django

I am trying to save data from an API with constant live updates. Hence, the data from the API will always container an existing dataset on my database. So I created a unique_together to prevent duplicate.
The model looks like:
class Autodamb(models.Model, Activity):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='highlight', null=True)
title = models.CharField(max_length=300)
url = models.URLField(null=True)
embed_donotuse = models.CharField(max_length=1000)
date = models.CharField(max_length=300)
thumbnail = models.URLField()
home_team_name = models.CharField(max_length=100)
away_team_name = models.CharField(max_length=100)
class Meta:
unique_together = ["title", "date"]
The views.py:
def save_damb_video(request):
r = requests.get('https://www.brainxo.com/com-api/v1/?apid=xxxxxxxxxxx')
# data = json.loads(r)
data = r.json()
for x in data:
title = x["title"]
embed_donotuse = x["embed"]
url = x["url"]
date = x["date"]
thumbnail = x["thumbnail"]
home_team_name = x["side1"]["name"]
away_team_name = x["side2"]["name"]
user = request.user
damb_data = Autodamb.objects.create(title=title, embed_donotuse=embed_donotuse, url=url, date=date, thumbnail=thumbnail, home_team_name=home_team_name, away_team_name=away_team_name, user=user)
damb_data.save()
return HttpResponse("DAMP API data successfully saved")
The problem is that the entire process would fail with a unique_together as some data already exist. Any Idea how to iterate and only save data not already existing while maintaining the unique constraint?

This will ignore rows that have an already existing combination of title and date and just insert the rest.
objects = []
for x in data:
...
damb_data = Autodamb(
title=title,
embed_donotuse=embed_donotuse,
url=url, date=date,
thumbnail=thumbnail,
home_team_name=home_team_name,
away_team_name=away_team_name,
user=user
)
objects.append(damb_data)
bulk_create(objects, ignore_conflicts=True)
PS. I would recommend check out Django Rest Framework if you're building an API in Django. It has Serializers for just this kind of thing.

Related

How to step wise post data to an object using the Django-Rest Framework?

I want to post an object with Django-Rest Framework to my backend. The object contains multiple nested objects, and the user inserts this information setp-wise via a React JS frontend.
class Story (models.Model):
title = models.CharField(max_length=100,blank=False)
content = models.TextField(blank=False)
date_posted = models.DateTimeField(default=timezone.now)
place = models.ForeignKey(Place, on_delete=models.PROTECT, null=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
images = models.FileField(upload_to='audio_stories/',validators=[validate_file_extension_image],
blank=False)
audio = models.FileField(upload_to='audio_stories/',validators=[validate_file_extension_audio], blank=False)
I now wonder how it is possible to send the data to the server step by step. Is there a way to initiate a story object at the very beginning of the creation process to generate its primary key and then add the other data to this key step by step, e.g. images and audio? I've already searched for the right term in the Django documentation, but I'm having difficulties to approach this topic.
I am happy for any clarification.
Just create multiple endpoints and serializers for your Story:
class StoryBaseSerializer(serializers.ModelSerializer):
title = serializers.CharField(...)
date_posted = serializers.DateTimeField(auto_now=True)
id = serializers.ReadOnlyField()
class Meta:
model = models.Story
fields = ['title', 'date_posted', 'id']
class StoryContentSerializer(serializers.ModelSerializer):
content = serializers.TextField(...)
images = serializers.ImageField(...)
id = serializers.ReadOnlyField()
class Meta:
model = models.Story
fields = ['content', 'images', 'id']
#and so on
then, create the endpoints:
# e.G. /api/v1/stories
CreateStoryView(views.ListCreateAPIView):
serializer_class = StoryBaseSerializer
# standard api code....
and then your views which modify the Story object:
# e.G. /api/v1/stories/content/<int:pk>
UpdateStoryContent(views.UpdateAPIView):
serializer_class = StoryBaseSerializer
def get_object(self):
story_id= self.kwargs['pk']
story = get_object_or_404(self.get_queryset(), id=story_id)
return story;
#and a view for each serializer you have defined.
Of course, another (and maybe much more elegant way) would be to define a second serializer (in addition to the StoryBaseSerializer) that makes all fields optional (non required, simply set the required flag in the serializer field to False) and updates the Story data with the fields that are set.
Something along the lines of this:
class UpdateStorySerializer(serializers.ModelSerializer):
content = serializers.TextField(required=False, ...)
images = serializers.ImageField(required=False...)
# all your other fields go here
id = serializers.ReadOnlyField()
class Meta:
model = models.Story
fields = ['title', 'date_posted', '...', 'id']
and then create a single update view, simply the same as above but probably at another endpoint, let's say api/v1/stories/<int:pk>. Now, within your react views, you simply post only the data you want to update. For example you post to api/v1/stories/17:
{
"content" : "... the content ...."
}
or:
{
"audio" : "blob data of your audio file"
}
This would only update the content of the story with the id 17.

Django ModelChoiceField Issue

I've got the following Situation, I have a rather large legacy model (which works nonetheless well) and need one of its fields as a distinct dropdown for one of my forms:
Legacy Table:
class SummaryView(models.Model):
...
Period = models.CharField(db_column='Period', max_length=10, blank=True, null=True)
...
def __str__(self):
return self.Period
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'MC_AUT_SummaryView'
Internal Model:
class BillCycle(models.Model):
...
Name = models.CharField(max_length=100, verbose_name='Name')
Period = models.CharField(max_length=10, null=True, blank=True)
Version = models.FloatField(verbose_name='Version', default=1.0)
Type = models.CharField(max_length=100, verbose_name='Type', choices=billcycle_type_choices)
Association = models.ForeignKey(BillCycleAssociation, on_delete=models.DO_NOTHING)
...
def __str__(self):
return self.Name
Since I don't want to connect them via a Foreign Key (as the SummaryView is not managed by Django) I tried a solution which I already used quite a few times. In my forms I create a ModelChoiceField which points to my Legacy Model:
class BillcycleModelForm(forms.ModelForm):
period_tmp = forms.ModelChoiceField(queryset=SummaryView.objects.values_list('Period', flat=True).distinct(),
required=False, label='Period')
....
class Meta:
model = BillCycle
fields = ['Name', 'Type', 'Association', 'period_tmp']
And in my view I try to over-write the Period Field from my internal Model with users form input:
def billcycle_create(request, template_name='XXX'):
form = BillcycleModelForm(request.POST or None)
data = request.POST.copy()
username = request.user
print("Data:")
print(data)
if form.is_valid():
initial_obj = form.save(commit=False)
initial_obj.ModifiedBy = username
initial_obj.Period = form.cleaned_data['period_tmp']
initial_obj.Status = 'Creating...'
print("initial object:")
print(initial_obj)
form.save()
....
So far so good:
Drop Down is rendered correctly
In my print Statement in the View ("data") I see that the desired infos are there:
'Type': ['Create/Delta'], 'Association': ['CP'], 'period_tmp': ['2019-12']
Still I get a Select a valid choice. That choice is not one of the available choices. Error in the forms. Any ideas??

Django advanced query - objects of a Foriegnkey?

I have a Django models where I have this :
class Patient(models.Model):
FirstName = models.CharField(max_length=264)
LastName = models.CharField(max_length=264)
Address = models.TextField(blank=True)
Telephone_no = models.CharField(max_length=100,blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name='patients')
class UploadedImages(models.Model):
patient = models.ForeignKey(Patient,on_delete=models.CASCADE,related_name='images')
original = models.ImageField(upload_to = user_directory_path, validators=[validate_file_extension],verbose_name = 'Image')
enhanced = models.ImageField(upload_to=analyses_directory_path, blank=True)
segmented = models.ImageField(upload_to=analyses_directory_path, blank=True)
class Processed(models.Model):
uploaded_image = models.ForeignKey(UploadedImages,on_delete=models.CASCADE,related_name='processed')
pre_analysed = models.ImageField(upload_to=analyses_directory_path, blank=True)
analysedimage = models.ImageField(upload_to=analyses_directory_path, blank=True)
so I want to make queries based on the current user which is user = request.user this is possible in the patient model case as I can make Patient.objects.filter(user=user)
but i can't make it the other 2 models
is there any idea how I can do this?
I didn't add the user FK as I thought I wouldn't need it but now I do?
do i need to add it ? can I make a query without adding the field ?
If you want to query across relationships, Django has explicit syntax for that. For example, to get all the UploadedImage objects for a specific user, use UploadedImage.objects.filter(patient__user=user).
Patient.objects.filter(user=user) returns a queryset, to get patient by user, assuming one Patient has only one user:
patient = Patient.objects.filter(user=user).first()
then you can do:
uploaded_images = patients.images.all()
for image in uploaded_images:
processed = image.processed.all()

How to connect a model with foreign key in a function which uploads a csv file in django?

I have a django web app where I want users to upload their data in the form of csv. I have created a function and a page where the user uploads csv, and I have appended the user to the model, but I want that user to be the foreign key for each of the files. From what I see, the data is being entered in the database just fine but I do not think it is properly connected with a foreign key because on analyzing the database it says there are no foreign keys connected, and just the value is being entered with no relation what so ever.
Here is my function:
def upload_batch(request):
template_name = 'classroom/teachers/upload.html'
prompt = {'order':'Order of csv should be first_name, last_name, email, ip_address, message'}
if request.method == "GET":
return render(request,template_name,prompt)
csv_file = request.FILES['file']
data_set = csv_file.read().decode('UTF-8')
io_string = io.StringIO(data_set)
next(io_string)
uploaded_by = request.user
for column in csv.reader(io_string,delimiter=',',quotechar='|'):
_, created = ItemBatch.objects.update_or_create(
name = column[0],
pid = column[1],
quantity = column[2],
length = column[3],
width = column[4],
height = column[5],
volume = column[6],
weight = column[7],
uploaded_by = uploaded_by
)
context = {}
return render(request,template_name,context)
Here is my model:
# item upload
class ItemBatch(models.Model):
# uploaded_by = models.ForeignKey(Teacher, on_delete=models.CASCADE, related_name='uploaded_by')
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='uploaded_by')
name = models.CharField(max_length=30)
pid = models.CharField(max_length=30)
quantity = models.CharField(max_length=30)
length = models.CharField(max_length=100, blank=True)
width = models.CharField(max_length=100, blank=True)
height = models.CharField(max_length=100, blank=True)
volume = models.CharField(max_length=100, blank=True)
weight = models.CharField(max_length=100, blank=True)
def __str__ (self):
return self.name
UPDATE: Here is a screenshot of the model in my database.As you can see, the uploaded_by is not being shown as a foreign key, just like it is being shown in this other working model.
My model in database:
Foreign key tab is empty.
My other working model with FK.
The code and the model looks fine.
Did you make some changes to the model in the past (and ran makemigrations, migrate)?
According to your screenshots there is no foreign key constraint on that table, which is strange since it seems to get populated correctly.
Did you refresh/synchronize the schema in the tool?

Pulling and showing data from associate table in Django rest framework

I am trying to build an API using Django Rest Framework. Which i am not familiar with. I want to know how I can pull data using references and associated tables. I have three models Users, Company and Reviews. I am storing and Auth_token in user table and I want to be able to pull reviews by a certain user by putting the auth token in the address bar.
my models are
class User(models.Model):
user_name = models.CharField(max_length=45)
email = models.CharField(max_length=255)
auth_Token = models.CharField(max_length=100,default=uuid.uuid4)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __str__(self):
return self.user_name
class Company(models.Model):
company_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __str__(self):
return self.company_name
class Review(models.Model):
title = models.CharField(max_length=64)
rating = models.IntegerField(blank=False)
summary = models.TextField(max_length=10000)
ip = models.GenericIPAddressField()
company = models.ForeignKey(Company)
user = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __str__(self):
return self.title
I am currently able to pull reviews using following apiview:
class ReviewView(APIView):
def get(self,request):
reviews = Review.objects.all()
serializer = ReviewSerializer(reviews, many=True)
return Response(serializer.data)
and the following serilaizer:
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ('title','company', 'rating', 'summary','user')
Please ignore the indentations. However this results in me getting back the company id and user id only. I basicaly want to know two things here.
First how do i pull data where auth token is passed as the url
url(r'^reviews/(?P<auth_Token>[\w-]+)/$', ReviewView.as_view()),
and second how do i display company name and user name instead of ids. Any help would be great. Thanks
How do I filter reviews based on the user's auth token?
I will suggest using a ReadOnlyModelViewSet. It will greatly reduce your view code and allow for easy filtering. Most of the mundane and tiring code of handling requests, validating and so on has already been written in these viewsets and therefore, you can just focus on your business logic rather than server side details.
Instead of using an auth_Token in the URL param itself (eg. reviews/XXX/), I have placed it as a query param (eg. reviews/?auth_Token=XXX). The reason behind this is because the URL param itself should return a specific review resource but you need to return a list of filtered reviews mapped to one user.
from rest_framework import viewsets
class ReviewView(viewsets.ReadOnlyModelViewSet):
serializer_class = ReviewSerializer
def get_queryset(self):
"""
This function is called whenever someone tries to retrieve reviews.
You do not need to worry about serialization or handling the response
as the viewset has set that up with your specified serializer_class
"""
auth_Token = self.query_params.get("auth_Token", None)
if auth_Token: # They provided an auth token so we need to filter.
reviews = Review.objects.filter(user__auth_Token=auth_Token)
else:
reviews = Review.objects.all()
return reviews
In your urls:
url(r'^reviews/$', ReviewView.as_view({"get":"list"})),
How do I show the company name and user name in retrieved reviews and not show their ids?
You need to use a SlugRelatedField (http://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield). This field allows you to replace the typical id with another attribute found in the associated table. Please see the new ReviewSerializer below:
class ReviewSerializer(serializers.ModelSerializer):
company = serializers.SlugRelatedField(read_only=True, slug_field="company_name")
user = serializers.SlugRelatedField(read_only=True, slug_field="user_name")
class Meta:
model = Review
fields = ('title','company', 'rating', 'summary','user')