Saving / accessing fields from Class methods (Django) - django

Appologies for the beginner question and/or stupidity - I'm learning as I go.... I'm trying to pass a user entered url of a PubMed article to access the metadata for that article. I'm using the following code, but I cannot access anything form the save method in he 'Entry' model. For example in my html form I can display {{entry.date_added }} in a form but not {{ entry.title}}. I suspect it's a simple answer but not obvious to me. Thanks for any help.
models.py
from django.db import models
from django.contrib.auth.models import User
import pubmed_lookup
from django.utils.html import strip_tags
class Topic(models.Model):
"""Broad topic to house articles"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""Return a string representation of the model"""
return self.text
class Entry(models.Model):
"""Enter and define article from topic"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
pub_med_url = models.URLField(unique=True)
date_added = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
query = self.pub_med_url
email = "david.hallsworth#hotmail.com"
lookup = pubmed_lookup.PubMedLookup(query, email)
publication = pubmed_lookup.Publication(lookup)
self.title = strip_tags(publication.title)
self.authors = publication.authors
self.first_author = publication.first_author
self.last_author = publication.last_author
self.journal = publication.journal
self.year = publication.year
self.month = publication.month
self.day = publication.day
self.url = publication.url
self.citation = publication.cite()
self.mini_citation = publication.cite_mini()
self.abstract = strip_tags(publication.abstract)
super().save(*args, **kwargs)
class Meta:
verbose_name_plural = 'articles'
def __str__(self):
return "{} - {} - {} - {} [{}]".format(self.year,
self.first_author, self.journal, self.title, str(self.pmid), )

In Django ORM, you have to manually specify all fields that need to be saved. Simply saving it as self.foo = bar in the save method is stored in the Entry instance object (=in memory), but not in the database. That is, there is no persistence. Specify all the fields that need to be saved in the model and run python manage.py makemigrations,python manage.py migrate. Assigning fields to the model is actually the task of designing the relational database.
class Entry(models.Model):
"""Enter and define article from topic"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
pub_med_url = models.URLField(unique=True)
date_added = models.DateTimeField(auto_now_add=True)
title = models.CharField(...)
authors = models.CharField(...)
...
def assign_some_data_from_pubmed(self):
email = "david.hallsworth#hotmail.com"
lookup = pubmed_lookup.PubMedLookup(query, email)
publication = pubmed_lookup.Publication(lookup)
self.title = strip_tags(publication.title)
self.authors = publication.authors
self.first_author = publication.first_author
self.last_author = publication.last_author
self.journal = publication.journal
self.year = publication.year
self.month = publication.month
self.day = publication.day
self.url = publication.url
self.citation = publication.cite()
self.mini_citation = publication.cite_mini()
self.abstract = strip_tags(publication.abstract)
Usage:
entry = Entry(...)
entry.assign_some_data_from_pubmed()
entry.save()

Related

django serializer error: images_data = self.context['request'].FILES KeyError: 'request'

models.py
#
from django.db import models
from user.models import User
from chat.models import TradeChatRoom, AuctionChatRoom
class Goods(models.Model):
class Meta:
db_table = 'Goods'
ordering = ['-created_at'] # 일단 추가해뒀습니다
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sell_goods')
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='buy_goods', null=True)
trade_room = models.ForeignKey(TradeChatRoom, on_delete=models.CASCADE)
auction_room = models.ForeignKey(AuctionChatRoom, on_delete=models.CASCADE)
title = models.CharField(max_length=256)
content = models.TextField()
category = models.CharField(max_length=32)
status = models.BooleanField(null=True)
predict_price = models.IntegerField()
start_price = models.IntegerField()
high_price = models.IntegerField(null=True)
start_date = models.DateField(null = True)
start_time = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(User, related_name='like_goods', null=True)
class GoodsImage(models.Model):
class Meta:
db_table = "GoodsImage"
goods = models.ForeignKey(Goods, on_delete=models.CASCADE)
image = models.ImageField(upload_to='goods/')
serializer.py
from rest_framework import serializers
from .models import Goods,GoodsImage
class GoodImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(use_url=True)
def get_image(self, obj):
image = obj.goods_set.all()
return GoodsPostSerializer(instance=image, many = True, context = self.context)
class Meta:
model = GoodsImage
field =('image',)
class GoodsPostSerializer(serializers.ModelSerializer):
image = GoodImageSerializer(many=True, read_only = True)
class Meta:
model = Goods
fields = (
'seller', 'buyer','auction_room','title','content',
'category','status','predict_price','start_price','high_price',
'trade_room','start_date','start_time','created_at','like','image',
)
read_only_fields = ("seller",)
def create(self, validated_data):
goods = Goods.objects.create(**validated_data)
images_data = self.context['request'].FILES
for image_date in images_data.getlist('image'):
GoodsImage.objects.create(goods = goods, image = image_date)
return goods
error
images_data = self.context['request'].FILES
KeyError: 'request'
I want to save multiple images, but I keep getting an error. I don't know what to do anymore.
I searched for a method and followed it, but it seems that I am the only one who gets an error.
Please help if you know how to solve this problem.
And I want to know if it is correct to put it in a list like "image":["12.jpeg,"13.jpeg] when inserting multiple images through postman.
It's hard not being able to solve this problem. please help me if you know the answer
Change GoodImageSerializer calling this:
GoodImageSerializer(instance=images, many = True, context={'request': request})
Then change your GoodsPostSerializer's create method like this:
def get_image(self, obj):
image = obj.goods_set.all()
request = self.context['request']
return GoodsPostSerializer(instance=image, many = True, context={'request': request})

Passing parent object into CreateView for a child object

I'm creating a dashboard to edit a tour app.
Per tour I have a child record in which I define steps. The 2 models look like this:
models.py
class Tour(models.Model):
tour_id = models.CharField(primary_key=True,unique=True, max_length=10)
country = models.ForeignKey(Countries, models.DO_NOTHING, db_column='country')
language = models.ForeignKey(Language, models.DO_NOTHING, db_column='language')
lastupddtm = models.DateTimeField(default=timezone.now)
productid = models.CharField(max_length=50)
title = models.CharField(max_length=50)
description = models.CharField(max_length=100)
descrlong = models.CharField(max_length=1000)
live = models.CharField(max_length=1)
image = models.ImageField(upload_to=upload_tour_image, storage=OverwriteStorage(), blank=True, null=True)
class Meta:
db_table = 'tour'
verbose_name_plural = "tour"
def get_language_flag(self):
return self.language.flag.url
def __str__(self):
return str(self.tour_id) + ' - ' + str(self.title) + ' - ' + str(self.description)
class Toursteps(models.Model):
# tour_id = models.OneToOneField(Tour, models.DO_NOTHING, db_column='tour_id')
tour = models.ForeignKey(Tour, related_name='toursteps', on_delete=models.CASCADE)
step = models.IntegerField(unique=True)
title = models.CharField(max_length=50)
description = models.CharField(max_length=100)
descrlong = models.CharField(max_length=1000)
audiotext = models.TextField()
latitude = models.FloatField()
longitude = models.FloatField()
radius = models.FloatField()
image = models.ImageField(upload_to=upload_tour_step_image, blank=True, null=True)
class Meta:
db_table = 'tourSteps'
verbose_name_plural = "tourSteps"
def __str__(self):
return str(self.tour) + "|" + str(self.step)
After I created a Tour, I go to a detail page. From there I can click a link to add a step for this tour.
This is where the problem is. I pass the tour_id as a variable into the url, but I can't find a way to pick it up in the CreateView of the step.
urls.py
urlpatterns = [
path('tour/<str:pk>/detail', views.TourDetailView.as_view(), name='tour_detail'),
path('tour/<str:pk>/edit', views.UpdateTourView.as_view(), name='tour_edit'),
path('tour/<str:pk>/remove', views.DeleteTourView.as_view(), name='tour_remove'),
path('tour/<str:tour_id>/step/new', views.CreateTourStepView.as_view(), name='tour_step_new')
]
Tour detail view
<p><span class="glyphicon glyphicon-plus"></span></p>
views.py
class CreateTourStepView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'tour_admin/tour_list.html'
success_url = '/'
form_class = TourStepForm
model = Toursteps
def get_context_data(self, **kwargs):
context = super(CreateTourStepView, self).get_context_data(**kwargs)
print(context['tour_id'])
return context
forms.py
class TourStepForm(forms.ModelForm):
class Meta():
model = Toursteps
#fields = '__all__'
exclude = ('tour',)
def form_valid(self, form):
if form.is_valid():
form.instance.tour_id = self.request.GET("tour_id")
form.instance.save()
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('tour_detail', kwargs={'pk':form.instance.tour_id})
First, your form_valid() and get_success_url() methods belong in your view, not in your form.
Second, the tour_id is passed to the view's kwargs, it's not a query parameter, hence not in self.request.GET. You can find it in self.kwargs.
Third, you need to actually fetch the Tour from your database, not just assign the tour_id. I could post to any tour_id if I wanted and there's no guarantee the tour_id belongs to an actual Tour object. Return a 404 if the tour doesn't exist. And if it exists, assign it to the tour step.
Finally, you should not assign to and save form.instance. You should get the instance using step = form.save(commit=False), then assign to step and save step.

how to populate django models randomly

I am following a tutorial online for Django. The presenter loads in random data as follows:
for i in xrange(100): Vote(link = Link.objects.order_by('?')[0],voter=a).save()
From what I could understand, it goes from 0 to 100 and creates a new vote. The vote object has a link object. I don't understand what the order_by('?') means.
Here is the model.py file:
from django.db import models
from django.contrib.auth.models import User
from django.db.models import Count
class LinkVoteCountManager(models.Manager):
def get_query_set(self):
return super(LinkVoteCountManager, self).get_query_set().annotate(
votes=Count('vote')).order_by("-votes")
class Link(models.Model):
title = models.CharField("Headline", max_length=100)
submitter = models.ForeignKey(User)
submitted_on = models.DateTimeField(auto_now=True)
rank_score = models.FloatField(default=0.0)
url = models.URLField("URL", max_length=250, blank=True)
description = models.TextField(blank=True)
with_votes = LinkVoteCountManager()
objects = models.Manager()
def __unicode__(self):
return self.title
class Vote(models.Model):
voter = models.ForeignKey(User)
link = models.ForeignKey(Link)
def __unicode__(self):
return "%s voted %s" %(self.voter.username, self.link.title)

Django-celery IntegrityError Column name not unique

I have this simple model that acts like a rsync config that is used to pre fill in the fields for a celery periodic task. The first time i create a new rsync config trough the model everything is okay and a new periodic task is being created without a problem. When i try and alter certain fields that will change the task fields such as task arguments, I'm getting a "IntegrityError column name is not unique" I feel that it has something to do with the model save method but im not sure how to get it Right. anyone got some ideas?
here is the model:
from django.forms import ModelForm
from djcelery.models import IntervalSchedule
from djcelery.models import PeriodicTask, IntervalSchedule
INTERVAL=(
('every=5','period 5 minutes'),
)
class Customer(models.Model):
"""(Customer description)"""
customername = models.CharField(blank=True, max_length=30)
emailaddress = models.EmailField()
phonenumber = models.CharField(blank=True, max_length=10)
class Meta:
verbose_name_plural = "Customer"
def __unicode__(self):
return self.customername
class RsyncConfig(models.Model):
"""(RsyncConfig description)"""
cname = models.ForeignKey(Customer)
rsyncname = models.CharField(blank=True, max_length=255)
interval=models.CharField(max_length=8,choices=INTERVAL)
fromip = models.IPAddressField(blank=True)
source_dir = models.CharField(blank=True, max_length=255)
destination_dir = models.CharField(blank=True, max_length=255)
rsync_args = models.CharField(blank=True, max_length=255)
class Meta:
verbose_name_plural = "Rsync Config"
def __unicode__(self):
return self.cname.customername
And here is the admin.py form.
from django.contrib import admin
from django import forms
from djcelery.models import PeriodicTask, IntervalSchedule
from newrsync.models import Customer,RsyncConfig
class CustomerAdmin(admin.ModelAdmin):
class Meta:
model = Customer
class RsyncConfigAdminForm(forms.ModelForm):
list_display = ('customername', 'rsyncname','source_dir','destination_dir')
class Meta:
model = RsyncConfig
def __init__(self, *args, **kwargs):
super(RsyncConfigAdminForm,self).__init__(*args, **kwargs)
def save(self, commit=True):
interval = IntervalSchedule.objects.get(every=5,period="minutes")
model = super(RsyncConfigAdminForm, self).save(commit = False)
model.cname = self.cleaned_data['cname']
model.rsyncname = self.cleaned_data['rsyncname']
model.fromip = self.cleaned_data['fromip']
model.source_dir = self.cleaned_data['source_dir']
model.destination_dir = self.cleaned_data['destination_dir']
model.rsync_args = self.cleaned_data['rsync_args']
if commit:
model.save()
PeriodicTask.objects.get_or_create(
interval=interval,
task='apps.mftconfig.tasks.rsync_proc',
args=['rsync',
model.rsync_args,
model.source_dir,
model.destination_dir],
kwargs = {},
name = (model.cname," ",model.rsyncname),
enabled=False
)
return model
class RsyncConfigAdmin(admin.ModelAdmin):
form = RsyncConfigAdminForm
list_display = ('cname', 'rsyncname','source_dir','destination_dir')
admin.site.register(Customer,CustomerAdmin)
admin.site.register(RsyncConfig,RsyncConfigAdmin)
I basically ended up doing a delete of the object right before i save a new version.It's Not perfect but at least i circumvent the unique restrain in the PeriodicTask model and now let's hope it won't bite me in the ass later on.
If anyone has any suggestions, please! ;-)
class RsyncConfigAdminForm(forms.ModelForm):
list_display = ('customername','rsyncname','source_dir','destination_dir')
class Meta:
model = RsyncConfig
def __init__(self, *args, **kwargs):
super(RsyncConfigAdminForm,self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = super(RsyncConfigAdminForm, self).save(commit = False)
instance.customername = self.cleaned_data['customername']
instance.rsyncname = self.cleaned_data['rsyncname']
instance.fromip = self.cleaned_data['fromip']
instance.source_dir = self.cleaned_data['source_dir']
instance.destination_dir = self.cleaned_data['destination_dir']
instance.rsync_args = self.cleaned_data['rsync_args']
interval = IntervalSchedule.objects.get(every=5,period="minutes")
p=PeriodicTask.objects.filter(name=instance.rsyncname)
p.delete()
PeriodicTask.objects.get_or_create(
interval=interval,
task='apps.mftconfig.tasks.rsync_proc',
args=['rsync',
instance.rsync_args,
instance.source_dir,
instance.destination_dir],
kwargs = {},
name = (instance.rsyncname),
enabled=True
)
if commit:
instance.save()
return instance

Non-database field in Django model

Is it possible to have a field in a Django model which does not get stored in the database.
For example:
class Book(models.Model):
title = models.CharField(max_length=75)
description models.CharField(max_length=255, blank=True)
pages = models.IntegerField()
none_db_field = ????
I could then do
book = Book.objects.get(pk=1)
book.none_db_field = 'some text...'
print book.none_db_field
Thanks
As long as you do not want the property to persist, I don't see why you can't create a property like you described. I actually do the same thing on certain models to determine which are editable.
class Email(EntryObj):
ts = models.DateTimeField(auto_now_add=True)
body = models.TextField(blank=True)
user = models.ForeignKey(User, blank=True, null=True)
editable = False
...
class Note(EntryObj):
ts = models.DateTimeField(auto_now_add=True)
note = models.TextField(blank=True)
user = models.ForeignKey(User, blank=True, null=True)
editable = True
Creating a property on the model will do this, but you won't be able to query on it.
Example:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def _get_full_name(self):
return "%s %s" % (self.first_name, self.last_name)
def _set_full_name(self, combined_name):
self.first_name, self.last_name = combined_name.split(' ', 1)
full_name = property(_get_full_name)
full_name_2 = property(_get_full_name, _set_full_name)
Usage:
from mysite.models import Person
a = Person(first_name='John', last_name='Lennon')
a.save()
a.full_name
'John Lennon'
# The "full_name" property hasn't provided a "set" method.
a.full_name = 'Paul McCartney'
Traceback (most recent call last):
...
AttributeError: can't set attribute
# But "full_name_2" has, and it can be used to initialise the class.
a2 = Person(full_name_2 = 'Paul McCartney')
a2.save()
a2.first_name
'Paul'
To make it an instance variable (so each instance gets its own copy), you'll want to do this
class Book(models.Model):
title = models.CharField(max_length=75)
#etc
def __init__(self, *args, **kwargs):
super(Foo, self).__init__(*args, **kwargs)
self.editable = False
Each Book will now have an editable that wont be persisted to the database
If you want i18n support:
# Created by BaiJiFeiLong#gmail.com at 2022/5/2
from typing import Optional
from django.db import models
from django.utils.translation import gettext_lazy as _
class Blog(models.Model):
title = models.CharField(max_length=128, unique=True, verbose_name=_("Title"))
content = models.TextField(verbose_name=_("Content"))
_visitors: Optional[int] = None
#property
def visitors(self):
return self._visitors
#visitors.setter
def visitors(self, value):
self._visitors = value
visitors.fget.short_description = _("Visitors")