Updating model objects in the database on Django - django

I am new to Django and for learning purposes I am trying to build my own site using the linkedn API to display my profile. The following is a a example of my code. To see the whole lot:
https://github.com/javiee/django-site
models.py
class Profile(models.Model):
user = models.ForeignKey(User)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name, self.last_name
class Education(models.Model):
user = models.ForeignKey(User)
school_name = models.CharField(max_length=100)
field_study = models.CharField(max_length=100)
degree = models.CharField(max_length=100)
start_date = models.CharField(max_length=20)
end_date = models.CharField(max_length=20)
and views.py
profile = Profile(first_name = content['firstName'],
last_name = content['lastName'],
user = request.user)
profile.save()
#Education model
content_educ = content['educations']['values']
for value in content_educ:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
education.save()
This all working but my problem is that everytime I check linkedn, the code saves all the objects again. What it would ideally do is to "update" fields based on the profile when the .save() method is called. I have read the next link https://docs.djangoproject.com/en/1.7/ref/models/instances/#saving-objects
but I dont manage to get it working, perhaps foreigns keys are not properly set so any advise/help/tip will be much appreciated. Thanks!

Use the update_or_create() method:
Education.objects.update_or_create(
school_name=value['schoolName'],
user = request.user,
defaults={'field_study': value['fieldOfStudy'],
'degree': value['degree'],
'start_date': value['startDate']['year'] ,
'end_date': value['endDate']['year']})

The problem you're having is that you're instantiating new Education instances in these lines:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
When Django goes and tries to save these new instances (instances for which id is not yet defined), Django goes ahead and inserts the records rather than doing the update you want.
To do an update, you can either try to get the record, catching the DoesNotExist exception:
try:
education = Education.objects.get(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
except Education.DoesNotExist:
education = Education(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
then apply whatever updates you want/need.
Or you can use get_or_create to do the same:
(education, created) = Education.objects.get_or_create(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
If you don't want to look up your instances by all of those values (they're AND-ed), but want to initialize new instances with certain values, you should look up the defaults keyword for get_or_create.
Or you can use update_or_create as suggested by catavaran.
edit: Or, if you just want to do a straight update of a record without getting it (this also works with multiple objects at once), you can use queryset.update
Education.objects.filter(attribute=value, ...).update(attribute2=value2, ...)

Related

Django: Run a function if certain condition is met

I have a Word model, where a user can add words and various fields, like this (shortened version):
class Word(models.Model):
target_word = models.CharField()
source_word = models.CharField()
add_to_review = models.BooleanField(default=True)
example_sentence = models.CharField()
image = models.ImageField(upload_to='images/',blank=True)
audio = models.FileField(upload_to='audio/',blank=True)
I also have a Flashcard model where a user can add and study flashcards. Here I show only the relevant code:
class FlashcardManager(models.Manager):
def create_flashcard(self, user, question, answer, deck_name):
try:
deck = Deck.objects.get(owner=user, name=deck_name)
except ObjectDoesNotExist:
deck = Deck(owner=user, name=deck_name)
deck.save()
self.create(owner=user, question=question, answer=answer,
deck=deck)
return deck
class Flashcard(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
question = models.TextField()
answer = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
last_shown_at = models.DateTimeField(auto_now_add=True)
next_due_date = models.DateTimeField(default=timezone.now)
difficulty = models.FloatField(default=2.5)
consec_correct_answers = models.IntegerField(default=0)
objects = FlashcardManager()
def __str__(self):
return self.question
When a user creates a new word, I want to give them the option of automatically creating a flashcard using the info provided. In other words, question = target_word, answer = source_word, and the deck name can either be some default value, or I can add a field to the Word model. Is there a way I can do this by using the add_to_review field of the Word model and the create_flashcard method of the FlashcardManager model? I've tried adding a function to my Word model that runs if add_to_review is true. I don't get any errors, but nothing happens.
I know one option would be to just combine the two models... but I do have my own reasons for keeping them seperate. I might still do that, but I was just wondering if what I am trying to do is possible / feasible? I'm not really sure what exactly to google, so haven't been able to find any info on this. So any advice would be great, thanks!
Not sure if this is the best way, but it worked. Initially I had problems with the signal not being called, but this got fixed when I moved it to my models.py file instead of having it separate in a signals.py file.
#receiver(post_save, sender=Word)
def flash_from_word(sender, created, instance, **kwargs):
if created:
flash_wanted = Word.objects.filter(add_to_review=True)
instance._meta.model.objects.all()
target_word = instance.target_word
source_word = instance.source_word
deck_name = instance.deck_name
user = instance.user
if flash_wanted:
flash = Flashcard.objects.create(owner=user, question=target_word,answer=source_word,deck=Deck.objects.get(name=deck_name,owner=user))
post_save.connect(flash_from_word, sender=Word)

Django Select Drop Down menu Flow

What I'm trying to do is a 2 tier search with drop down menus using Select widget, the results will be a listing of the fields from my Meta.model. the first Tier is a a State listing from State.model. Upon a select it is supposed to list out all of the cities with in the selected state, the problem I'm having (and I think its due to my lack of knowledge) is that the city listing is not filtered but a listing of all cities in my database regardless of state. I'm not sure where or how to pass my variable to be able invoke my .filter() statement.
models.py
class Meta(models.Model):
rcabbr = models.CharField(max_length = 15)
slug = models.SlugField(unique=False)
state = models.ForeignKey('State')
rc_state = models.CharField(max_length = 3)
oerp = models.CharField(max_length=18)
subgrp = models.SlugField()
sonus_pic = models.CharField(max_length=8)
ems = models.CharField(max_length=14)
agc = models.CharField(max_length=14)
def __unicode__(self):
return self.rcabbr
class State(models.Model):
name = models.CharField(max_length=2)
slug = models.SlugField(unique=True)
state_long = models.CharField(max_length=15)
owning_site = models.CharField(max_length=12)
def __unicode__(self):
return self.name
return self.state_long
forms.py
class states(forms.Form):
invent = [(k.name,k.state_long) for k in State.objects.all()]
rclist = forms.ChoiceField(widget=forms.Select, choices=invent)
class rateCenter(forms.Form):
invention = [(k.id,k.rcabbr,k.rc_state) for k in Meta.objects.all()]
rcviews = forms.ChoiceField(widget=forms.Select, choices=invention)
views.py
def StateAll(request):
""" This lists out all of the states within the database that
are served"""
rclist = states()
return render(request, 'statelist.html',{'rclist': rclist})
def RcView(request):
""" this should list all the rateCenters within
the state that was selected in StateAll() """
rclist = request.GET['rclist']
forms = rateCenter()
return render(request, 'rclist.html',{'forms': forms})
Logic tells me I should to do my .filter() statement in the forms.py but unsure how to pass the result from the request.GET in StateAll() view. I do have the debug_toolbar installed so I can see the variable u'rclist' and the value u'LA' (my test state). I had this working 100% using hyperlinks however the size of my test database is miniscule in comparison to what is going to be in the production version and HREF's are just not possible.
my understanding is:
ChainedForeignKey(LinkedModel, LinkedModel.field = "field in first Tier", chained_model_field = "current model_field")
so simple model should I think be something like this?
def State(model.models):
name = models.CharField(max_length=20) #this is the state abbreviation
state_long = models.CharFeild(max_length=20)#this is state long form
def Meta(model.models):
state = models.CharField(max_length=20)
slug = models.SlugField(unique = False) #same values as rcabbr
rcabbr = ChainedForeignKey(State, chained_field = "state_long",
chained_model_field = "slug")
.....
Does that look about right........so the First Field in the drop down should be the State_long, once selected the next should be the slug?. at which time the slug should be passed to my urls and the views for the that final page.
I am going to try this however I'm not 100% sure how to do my views and if I need to do something with forms page or does this cover it? The documentation is not user friendly for someone new to this so any input would be most appreciated!
There are many third party libraries django-smart-selects and dajax come to mind - that will automate this for you along with provide you the necessary javascript to filter the form fields on the fly.
If you are investigating those, here is how you would do it with just the django forms:
class RateCenterForm(forms.Form):
rate_center = forms.ModelChoiceField(queryset=Meta.objects.all())
def __init__(self, *args, **kwargs):
state = kwargs.pop('state')
super(RaterCenterForm, self).__init__(*args, **kwargs)
self.fields['rate_center'].queryset = Meta.objects.filter(state=state)
A ModelChoiceField is a select drop down that takes its values from a model.
Now in your view, you would call it like this:
def show_rate_centers(request):
form = RateCenterForm(state='SomeState')
# .. your normal logic here

How to save many to many relationship?

I'm almost using following code(I striped down a little bit), I use auto complete light to load users and users can insert different people name(users) seperated by comma. the problem is when I try to save I get following error
ValueError at /write/
"<Article: test1>" needs to have a value for field "article" before this many-to-many relationship can be used.
models.py
class Article(models.Model):
author = models.ForeignKey(User,)
people = models.ManyToManyField(User, related_name="with", null=True,)
content = models.TextField()
forms.py
class ArticleForm(forms.ModelForm):
people = forms.CharField(widget=autocomplete_light.TextWidget('UserAutocomplete'))
class Meta:
model = Article
views.py
def write(request):
if request.POST:
form = ArticleForm(request.POST)
if form.is_valid():
content = form.cleaned_data['content']
user = User.objects.get(username=request.user.username)
people_str = form.cleaned_data['accompanied']
people = [x.strip() for x in accompanied_str.split(',')]
article = Article(
content = content,
author = user,
)
for username in accompanied:
user = User.objects.get(username=username)
article.people.add(user)
article.save()
return HttpResponseRedirect('/success/')
In order to create a relation you need the ids of both side. The newly created article has no id yet. If you save the article first and then add people to it it should work fine.
article = Article(
content = content,
author = user,
)
article.save()
article.add(*User.objects.filter(username__in=accompanied))
The process of adding people can be cheaper by getting all users that have a username from the list of accompanied in one fetch.

How do you set the initial value for a ManyToMany field in django?

I am using a ModelForm to create a form, and I have gotten the initial values set for every field in the form except for the one that is a ManyToMany field.
I understand that I need to give it a list, but I can't get it to work. My code in my view right now is:
userProfile = request.user.get_profile()
employer = userProfile.employer
bar_memberships = userProfile.barmembership.all()
profileForm = ProfileForm(
initial = {'employer': employer, 'barmembership' : bar_memberships})
But that doesn't work. Am I missing something here?
Per request in the comments, here's the relevant parts of my model:
# a class where bar memberships are held and handled.
class BarMembership(models.Model):
barMembershipUUID = models.AutoField("a unique ID for each bar membership",
primary_key=True)
barMembership = USStateField("the two letter state abbreviation of a bar membership")
def __unicode__(self):
return self.get_barMembership_display()
class Meta:
verbose_name = "bar membership"
db_table = "BarMembership"
ordering = ["barMembership"]
And the user profile that's being extended:
# a class to extend the User class with the fields we need.
class UserProfile(models.Model):
userProfileUUID = models.AutoField("a unique ID for each user profile",
primary_key=True)
user = models.ForeignKey(User,
verbose_name="the user this model extends",
unique=True)
employer = models.CharField("the user's employer",
max_length=100,
blank=True)
barmembership = models.ManyToManyField(BarMembership,
verbose_name="the bar memberships held by the user",
blank=True,
null=True)
Hope this helps.
OK, I finally figured this out. Good lord, sometimes the solutions are way too easy.
I need to be doing:
profileForm = ProfileForm(instance = userProfile)
I made that change, and now everything works.
Although the answer by mlissner might work in some cases, I do not think it is what you want. The keyword "instance" is meant for updating an existing record.
Referring to your attempt to use the keyword "initial", just change the line to:
bar_memberships = userProfile.barmembership.all().values_list('pk', flat=True)
I have not tested this with your code, but I use something similar in my code and it works.

How to get logged-in user info in Django's forms.py

I have created a Profile model including the Gender info. There is also models called Dorm and Registration (not used for user registration) like this:
class Registration(models.Model):
user = models.ForeignKey(User)
pref1 = models.ForeignKey(Dorm, related_name="pref1",verbose_name=u"Preference 1",null=True)
...
friend1 = models.CharField(u"Friend 1", max_length=15,blank=True)
class Dorm(models.Model):
name = models.ForeignKey(Building)
gender = models.CharField(u"Gender", max_length=1, blank=True, choices=GENDER_CHOICES)
Now, i am trying to generate a form for this Registration model with forms.ModelForm like this:
class RegistrationForm(forms.ModelForm):
dorms = Dorm.objects.filter(gender='M')
pref1 = forms.ModelChoiceField(queryset=dorms, empty_label=None)
...
class Meta:
model = Registration
exclude = ('user')
as you can see in the second line, i am querying the dorms with a hardcoded gender value M. Instead of the hardcoded value, I need to get the users' gender, and query the database with that gender information.
I have been searching the documentation but I could not find anything. Can you help me? How can I learn the logged-in User' profile information in Django Forms?
So without using some sort of monkeying around of the init function a "form_factory" is probably the best approach.
def RegFormFactory(user)
dorms = Form.objects.filter(gender = "user.gender")
class _RegistrationForm(forms.ModelForm):
pref1 = forms.ModelChoiceField(queryset = dorms, empty_label=None)
class Meta:
model = Registration
exclude = ('user')
return _RegistrationForm
then use:
formclass = RegFormFactory(user)
form_inst = formclass()
...
form_inst = formclass(request.POST)
This is described very nicely in a blog post here: So you want a dynamic form.
James Bennett wrote a blog post that should explain perfectly what you need: So you want a dynamic form