How to proper save documents in MongoDB using Djongo - django

I am facing a problem in which I cannot save documents inside MongoDB with Django.
The error follows:
AttributeError: 'NoneType' object has no attribute 'attname'
With the help of the library Djongo I made these models:
from djongo import models
from django.utils import timezone
class LatLng(models.Model):
latitude = models.FloatField(null=False)
longitude = models.FloatField(null=False,)
def __init__(self,latitude, longitude):
self.latitude = latitude
self.longitude = longitude
class Meta:
abstract = True
class Parameters(models.Model):
cond1= models.IntegerField(null=False,)
cond2= models.IntegerField(null=False,)
cond3= models.IntegerField()
class Meta:
abstract = True
class MyModel(models.Model):
name = models.CharField(max_length=150, null=False)
des= models.CharField(max_length=500)
pub_date = models.DateTimeField(editable=False)
mod_date = models.DateTimeField()
parameters = models.EmbeddedField(
model_container=Parameters
)
wp= models.ArrayField(
model_container=LatLng,
null=False
)
objects = models.DjongoManager()
def __init__(self, name, parameters, wp,des=""):
self.name = name
self.parameters = parameters
self.waypoints = waypoints
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if self.id is None:
self.pub_date = timezone.now()
self.mod_date = timezone.now()
return super(MyModel, self).save(*args, **kwargs)
def __str__(self):
return self.name
My API looks like:
def get_wp(pls):
wps = []
for pin pls:
coord = LatLng(latitude=p['latitude'], longitude=p['longitude'])
wps.append(coord)
return wps
#api_view(['POST'])
def save(request):
data = json.loads(request.body.decode('utf-8'))
scores = Parameters(cond1=data['cond1'], cond2=data['cond2'])
wps = get_wp(data['pls'])
obj = MyModel(name=data['name'],parameters=scores, waypoints=wps)
print("--> {}".format(obj.name)) #working fine
itinerary.save() ## it dies here
return JsonResponse({})
I don't know what I'm making wrong. Since this is my first time with Django (using MongoDB), any suggestions about my code are really appreciated.

Try removing LatLng __init__() or if still needed then try:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Do your changes here

Related

Django REST Serializers - passing a value when creating a new object

I'm having problems in passing an argument when creating an object with Django REST serializers.
models.py
class Project(models.Model):
name = models.CharField(max_length=200, unique=False)
description = models.TextField()
...
class Hypothesis(models.Model):
hypothesis = models.CharField(max_length=200, unique=False)
project = models.ManyToManyField(Project)
test_conducted = models.ManyToManyField('Interview', through='HypothesesFeedback')
...
serializers.py
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ['name','description','company_name']
def __init__(self, *args, **kwargs):
super(ProjectSerializer, self).__init__(*args, **kwargs)
class HypothesisSerializer(serializers.ModelSerializer):
class Meta:
model = Hypothesis
fields = ['hypothesis','area','details', 'project']
def get_alternate_name(self, obj):
project = self.context["project_id"]
views.py
class ProjectRestCreate(LoginRequiredMixin, generics.ListCreateAPIView):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
...
class HypothesisRestCreate(LoginRequiredMixin, generics.ListCreateAPIView):
queryset = Hypothesis.objects.all()
serializer_class = HypothesisSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context["project_id"] = 8 #self.kwargs['project_id']
return context
...
I'm currently unable to default the project id when creating a new object for class hypothesis. In the example above, I'm hardcoding a value just for test purposes, but what I'd need to reach is that when I create a new hypothesis starting from a given project page, the project is automatically filled, rather than the user having to manually select it.
Using Django, rather than Django REST, I'd be able to achieve that using the code below:
class HypothesisCreate(generic.CreateView):
model = Hypothesis
form_class = HypothesisForm
template_name = 'new_hypothesis.html'
def form_valid(self, form):
obj = form.save()
project = form.data['project']
p = Project.objects.filter(id=project)
obj.project.set(p)
return super(HypothesisCreate, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(HypothesisCreate, self).get_context_data(**kwargs)
context['p_id'] = self.kwargs['project']
return context
def get_success_url(self, **kwargs):
return reverse('project_detail', kwargs={'pk': self.kwargs['project']})
Any idea on how to reach the same with Django REST serializers?
EDIT #1
models.py
class Project(models.Model):
name = models.CharField(max_length=200, unique=False)
description = models.TextField()
...
class Hypothesis(models.Model):
hypothesis = models.CharField(max_length=200, unique=False)
project = models.ForeignKey(Project, on_delete= models.CASCADE)
test_conducted = models.ManyToManyField('Interview', through='HypothesesFeedback')
...
using Django rather than Django REST, I achieve the defaulting of the project when creating a new hypothesis, using get_context_data:
VIEW:
class HypothesisCreate(generic.CreateView):
model = Hypothesis
form_class = HypothesisForm
template_name = 'new_hypothesis.html'
def form_valid(self, form):
obj = form.save()
project = form.data['project']
p = Project.objects.filter(id=project)
obj.project.set(p)
return super(HypothesisCreate, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(HypothesisCreate, self).get_context_data(**kwargs)
context['p_id'] = self.kwargs['project']
return context
def get_success_url(self, **kwargs):
return reverse('project_detail', kwargs={'pk': self.kwargs['project']})
FORM:
class HypothesisForm(ModelForm):
class Meta:
model = Hypothesis
fields = ['hypothesis','area','details']
def __init__(self, *args, **kwargs):
super(HypothesisForm, self).__init__(*args, **kwargs)
self.fields["project"] = forms.CharField(widget=forms.HiddenInput())
I tried doing the same with the serializer, but without success.
VIEW:
class HypothesisRestCreate(LoginRequiredMixin, generics.ListCreateAPIView):
queryset = Hypothesis.objects.all()
serializer_class = HypothesisSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context["project_id"] = 8 #self.kwargs['project_id']
return context
SERIALIZER:
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ['name','description','company_name']
def __init__(self, *args, **kwargs):
super(ProjectSerializer, self).__init__(*args, **kwargs)
class HypothesisSerializer(serializers.ModelSerializer):
class Meta:
model = Hypothesis
fields = ['hypothesis','area','details', 'project'] #
def get_alternate_name(self, obj):
project = self.context["project_id"]
any idea what should I do differently?
Modify the HypothesisRestCreate as following
class HypothesisRestCreate(LoginRequiredMixin, generics.ListCreateAPIView):
queryset = Hypothesis.objects.all()
serializer_class = HypothesisSerializer
def create(self, request, *args, **kwargs):
request.data['project'] = self.kwargs['project_id']
return super(HypothesisRestCreate, self).create(request, *args, **kwargs)
# def get_serializer_context(self): -- dont need for this purpose
and HypothesisSerializer as following
class HypothesisSerializer(serializers.ModelSerializer):
class Meta:
model = Hypothesis
fields = ['hypothesis','area','details', 'project']
# def get_alternate_name(self, obj): --dont need for this purpose

ProgrammingError at /cart/address/ column order_useraddress.type does not exist

I am trying to create an eCommerce application. And for useraddress (Billing and shipping ) want something like below. Here have a model called Order and UserAddress , which is
class Order(models.Model):
cart = models.ForeignKey(Cart,on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
start_date = models.DateTimeField(auto_now_add=True)
ordered_date = models.DateTimeField()
ordered_total = models.PositiveIntegerField()
shipping_price = models.PositiveIntegerField(default=0)
ordered = models.BooleanField(default=False)
billing_address = models.ForeignKey(UserAddress,related_name='billing_address',on_delete=models.CASCADE)
shipping_address = models.ForeignKey(UserAddress,related_name='shipping_address',on_delete=models.CASCADE,default=None)
and
class UserAddress(models.Model):
BILLING = 'billing'
SHIPPING = 'shipping'
ADDRESS_TYPE = (
(BILLING , 'Billing'),
(SHIPPING, 'Shipping')
)
user = models.ForeignKey(UserCheckout,
on_delete=models.CASCADE)
name = models.CharField(max_length=50)
phone = models.CharField(max_length=21,null=True)
street_address = models.CharField(max_length=50)
home_address = models.CharField(max_length=50)
type = models.CharField(max_length=100,choices=ADDRESS_TYPE)
def __str__(self):
return self.user
def get_full_address(self):
return '{0}, {1},{2}'.format(self.name ,self.user,self.phone )
And my View is
class AddressFormView(FormView):
form_class = AddressForm
template_name = 'orders/address_select.html'
def dispatch(self, request, *args, **kwargs):
b_address, s_address = self.get_address()
if not (b_address.exists() and s_address.exists()):
messages.success(self.request, 'Please add an address before continuing')
return redirect('add_address') # redirect before checkout
return super(AddressFormView, self).dispatch(request, *args, **kwargs)
def get_address(self, *args, **kwargs):
user_checkout = self.request.session['user_checkout_id']
b_address = UserAddress.objects.filter(
type=UserAddress.BILLING, user_id=user_checkout)
s_address = UserAddress.objects.filter(
type=UserAddress.SHIPPING, user_id=user_checkout)
return b_address, s_address
def get_form(self):
form = super(AddressFormView, self).get_form()
b_address, s_address = self.get_address()
form.fields['billing_address'].queryset = b_address
form.fields['shipping_address'].queryset = s_address
return form
def form_valid(self, form, *args, **kwargs):
billing_address = form.cleaned_data['billing_address']
shipping_address = form.cleaned_data['shipping_address']
self.request.session['billing_address_id'] = billing_address.id
self.request.session['shipping_address_id'] = shipping_address.id
return super(AddressFormView, self).form_valid(form, *args, **kwargs)
def get_success_url(self):
return reverse('checkout')
And the above view used a form , which is
class AddressForm(forms.Form):
billing_address = forms.ModelChoiceField(queryset=UserAddress.objects.filter(type=UserAddress.BILLING),empty_label=None,widget=forms.RadioSelect)
shipping_address = forms.ModelChoiceField(queryset=UserAddress.objects.filter(type=UserAddress.SHIPPING),empty_label=None,widget=forms.RadioSelect)
But Now when i fetching the url http://127.0.0.1:8000/cart/address/ i have the above error. The code is form a github repo which is working correctly. But in my project i am using django Allauth package and Custom User Model, which the github project that i am following didn't use. I assume , this could be an issue , because the github repo didn't use that. So is there any hints or idea or any new way to do it.
The table order_useraddress in your database does not have the type column.
Have you tried migrating/updating the database?
python manage.py makemigrations
python manage.py migrate

Save calculated value as a model field

As a follow up to my eariler question I have a new one. How can I save this calculated value as a model field. I would like to use it in my views and templates to order list by this field.
My models:
class Tournament(models.Model):
name = models.CharField(max_length=100)
date = models.DateTimeField('date')
player_num = models.IntegerField(verbose_name="")
points = models.FloatField(default=1000.00)
def get_rating(self):
return self.points / 1000.00
class TournamentStandings(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
player_place = models.FloatField(verbose_name=u"")
player_points = models.FloatField(verbose_name="",
blank=True) #added for testing to save the calculated value in it
#property
def get_player_points(self, obj):
return obj.tournament.player_num * obj.tournament.get_rating() -
obj.tournament.get_rating()*(obj.player_place - 1.00)
def save(self, *args, **kwargs):
self.player_points = self.get_player_points
super(TournamentStandings, self).save(*args, **kwargs)
def __float__(self):
return self.player_points
Funny as on the admin list I have a column where player_points are calculated correctly but when I add a new model instance and try to save it I get this error : 'TournamentStandings' object has no attribute 'get_player_points'. Is it bacause I am trying to do a "self" save and my calculation is (self, obj) ?? Any hints are wellcome.
Posting a working solution to my problem. No need for parentheses.
First I have fixed Tournament model, so I could save get_rating as a field:
class Tournament(models.Model):
name = models.CharField(max_length=100)
rating = models.FloatField(verbose_name="Rating", blank=True)
#property
def get_rating(self):
return (self.points) / (1000.00)
def save(self, *args, **kwargs):
self.rating = self.get_rating
super(Tournament, self).save(*args, **kwargs)
def __float__(self):
return self.rating
When I had this I tried to copy it to second model. Problem was that I could not get it to work due to related obj I was calling in my calculation. But! I have managed to assign this values to variables inside get_player_points and now all is working as intended:
class TournamentStandings(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
player_place = models.FloatField(verbose_name="")
player_points = models.FloatField(verbose_name="", blank=True)
#property
def get_player_points(self):
player_num = float(self.tournament.player_num)
rating = float(self.tournament.rating)
player_points = float(rating*player_num-rating*(self.player_place - 1.00))
return player_points
def save(self, *args, **kwargs):
self.player_points = self.get_player_points
super(TournamentStandings, self).save(*args, **kwargs)
def __float__(self):
return self.player_points
And this works! Any thoughts on improvements I could make are wellcome ofc.
get_player_points() is a method and requires parentheses.
def save(self, *args, **kwargs):
self.player_points = self.get_player_points()

How to join models in Python djangorestframework

I am trying to joint two models in django-rest-framework.
My code isn't throwing any error but also it isn't showing other model fields that need to be joined.
Below is my code snippet:
Serializer:
class CompaniesSerializer(serializers.ModelSerializer):
class Meta:
model = Companies
fields = ('id', 'title', 'category')
class JobhistorySerializer(serializers.ModelSerializer):
companies = CompaniesSerializer(many=True,read_only=True)
class Meta:
model = Jobhistory
fields = ('id', 'title', 'company_id', 'companies')
View .
class UserJobs(generics.ListAPIView):
serializer_class = JobhistorySerializer()
def get_queryset(self):
user_id = self.kwargs['user_id']
data = Jobhistory.objects.filter(user_id=user_id)
return data
model:
class Companies(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=100, blank=True, default='')
category = models.CharField(max_length=30, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
title = self.title or False
category = self.category or False
super(Companies, self).save(*args, **kwargs)
class Jobhistory(models.Model):
id = models.AutoField(primary_key=True)
company_id = models.ForeignKey(Companies)
title = models.CharField(max_length=100, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
company_id = self.company_id or False
title = self.title or False
super(Jobhistory, self).save(*args, **kwargs)
Thanks in advance. Any help will be appreciated.
In your views, you have
serializer_class = JobHistorySerializer()
Remove the parenthesis from this.
The reason for this is apparent in the GenericAPIView, specifically the get_serializer() and get_serializer_class() methods:
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
As you can see in get_serializer, it initializes that serializer class with args and kwargs that aren't provided in your view code.

Django: Change Choice selection from FK in a Form linked to an UpdateView

The Model flow Topic -> Section -> Article.
I am building an Update View for my FAQ project to update already created Articles. I want the Form to provide a selection of Sections based on the Topic the Article was created under. As I already have the Articles PK passed in through the URL I was hoping to use it to walk back up to the Topic when creating my filter. I am receiving an object has no attribute ‘section’ error when the template is attempting to render the form on line self.fields['section'].queryset = Section.objects.filter(topic_id=self.section.topic.id) in the UpdateAriticleForm. I need help to figure out my query filter.
The URL:
url(r'^ironfaq/article/update/(?P<pk>\d+)/$', ArticleUpdateView.as_view()),
The Form:
from django import forms
from .models import Topic, Section, Article
class CreateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Section.objects.none())
def __init__(self, *args, **kwargs):
topic_pk = kwargs.pop('topic_pk')
super(CreateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id=topic_pk)
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
class UpdateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Section.objects.none())
def __init__(self, *args, **kwargs):
super(UpdateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id=self.section.topic.id)
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
The View:
class ArticleUpdateView(UpdateView):
model = Article
form_class = UpdateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
article.activity_user = self.request.user.username
article.activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%d" % (article.section.topic.slug,article.section.slug,article.id)
return super(ArticleUpdateView,self).form_valid(form)
The Models:
class Topic(Audit):
name = models.CharField(max_length=255)
icon = models.CharField(max_length=25,blank=True,null=True)
sort = models.SmallIntegerField()
slug = models.SlugField()
class Meta:
verbose_name_plural = "topics"
def __str__(self):
return self.name
class Section(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "sections"
def __str__(self):
return self.name
class Article(Audit):
title = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
section = models.ForeignKey(Section,on_delete=models.CASCADE)
answer = models.TextField()
vote_up = models.IntegerField(default=0)
vote_down = models.IntegerField(default=0)
view_count = models.IntegerField(default=0)
class Meta:
verbose_name_plural = "articles"
def __str__(self):
return self.title
The answer to the this issue was not passing 'pk' as a argument to the form and to add get_form_kwargs to the view to enable the form to see the 'pk' passed in the URL.
Form:
class UpdateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Article.objects.none())
def __init__(self, pk, *args, **kwargs):
super(UpdateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id__exact=Article.objects.filter(id=pk).first().section.topic.id)
View:
class ArticleUpdateView(UpdateView):
model = Article
form_class = UpdateArticleForm
template_name = "faq/form_create.html"
def get_form_kwargs(self):
kwargs = super(ArticleUpdateView,self).get_form_kwargs()
kwargs.update(self.kwargs)
return kwargs
def form_valid(self, form):
article = form.save(commit=False)
article.activity_user = self.request.user.username
article.activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%d" % (article.section.topic.slug,article.section.slug,article.id)
return super(ArticleUpdateView,self).form_valid(form)