created data in One2many field also serve as data on other module - python-2.7

I want to in every time I created a data on One2many field, at the same time I want it to save as data on my maintenance.equipment. I try to find the solution on other module in addons but i didn't yet find the answer.
The scenario is, before validating the shipment in my product, I need to input serial number on it. Each serial number I created to that product at the same time serves as my equipment name.
and that serial number which is sample1010, I need it to become my equipment name in module maintenance.equipment. I expected it to display here in my equipment.
All i taught is only i need to do is creating Many2one and One2many fields like this
class StockPackOperation(models.Model):
_inherit = 'stock.pack.operation'
lines_ids = fields.One2many('maintenance.equipment', 'lines_id')
sample = fields.Char(string="Sample")
class MaintenanceEquipment(models.Model):
_inherit = 'maintenance.equipment'
lines_id = fields.Many2one('stock.pack.operation')
but nothings happen. Any help or suggestion or advice please. I need to do this. Thanks in advice masters. Anw I am new in odoo.

This can be achieved by inheriting stock.pack.operation.lot class, as entered serial numbers are stored in this class with lot_name (in case of incoming shipment) and with lot_id (in case of outgoing shipment).
You will not need to care about outgoing shipment, as in outgoing shipment we select already existing serial numbers.
class StockPackOperationLot(models.Model):
_inherit = 'stock.pack.operation.lot'
#api.model
def create(self, vals):
res = super(StockPackOperationLot, self).create(vals)
if vals.get('lot_name'):
self.env['maintenance.equipment'].create({'name': vals.get('lot_name')})
return res
#api.multi
def write(self, vals):
if vals.get('lot_name'):
lot_name = self.lot_name
equipment_id = self.env['maintenance.equipment'].search([('name', '=', lot_name)])
res = super(StockPackOperationLot, self).write(vals)
equipment_id.name = vals.get('lot_name')
return res
else:
return super(StockPackOperationLot, self).write(vals)
For this functionality to work properly, you will need to make sure equipment name is unique, else you will need to store related equipment id in each stock.pack.operation.lot record, so that when user edits serial number, equipment is also updated, when there is no unique constraint on name of equipment.
hope this helps you...

Related

Django Model - CharField as concatenation of other fields

I'm learning Django and looking for a best practice:
Imagine I have a model for a mobile phone device:
class Device(models.Model):
vendor = models.CharField(max_length=100)
line = models.CharField(max_length=100, blank=True)
model = models.CharField(max_length=100)
Let's say I create an object like this:
Device.objects.create(vendor = "Apple",
line = "iPhone",
model = "SE"
)
or without "line":
Device.objects.create(vendor = "Xiaomi",
model = "Mi 6"
)
Then I'd like to track sales in my shop for every device, so I create a model for a "Deal" (I track only the deal date and the device sold, device as a ForeignKey):
class Deal(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
deal_date = models.DateTimeField(default=None)
Question:
What is the best way to create a "Deal" object, if I want to query "Device" by its full, concatenated name, e.g. "Apple iPhone SE" or "Xiaomi Mi 6"?
I've found something similar in Django database entry created by concatenation of two fields , however not sure if it's the right path in my case.
My best guess is something like this (where "name" is a concatenated field):
de = Device.objects.get(name = "Apple iPhone SE")
Deal.objects.create(device = de,
deal_date = datetime(2018, 4, 26, 15, 28)
)
What is the correct way to do this task? Many thanks for your help!
Thanks for your advice guys, searching a little bit more I've found an answer appropriate in my case:
what I did is I tweaked save() method, which now populates a field automatically as a concatenation of 3 other fields.
#propertywas usefull in this case too
Supposing that you var name contains your text search criteria, and usign your data models, you could use annotation to create a field to each object returned by your query set, and then filter using this field
You could try some as follow (it is not tested)
import datetime
from django.db.models import F
from your.app.models import Deal, Device
# supposing is in your view, but like sounds as a model method
def my_view(request, *args, **kwargs)
name = request.POST.get('name')
device_qs = Decive.objects.all().annotate(text_concatenated='{} {} {}'.format(F('vendor'), F('line'), F('model'))).filter(text_concatenated=name)
try:
device = device_qs.get()
except Device.DoesNotExist:
# to manage the scenario when doesn't exist any match
# here you manage this situation
pass
except Device.MultipleObjectsReturned:
# to manage the scenario when various devices have the same 'text concatenated', i dont know, maybe data model should be improved
# here you manage this situation
device = device_qs.first()
deal = Deal.objects.create(device=device, deal_date=datetime.datetime.now())
# build your response and return it

exclude a query result in another query

Here i want to do is that ,i want to list all the person who didn't blocked me.Here in the table Blocked there is two columns name
who and whose . In whose column i store the id of the person whom i blocked and in the who column i store my id. Now i want to do that, when the blocked person click on
view-person button in my web page he cannot see profile of the person one who blocked him.
when i did this query blocked_list = Blocked.objects.filter(whose = user_id). Now i got the list of the persons who blocked me. Now i want to exclude all this person from this query total_profiles = persons.objects.all().exclude(blocked_list). How can i do this.
models.py
class persons(models.Model):
full_name = models.CharField(max_length=200)
class blocked(models.Model):
who = models.ForeignKey(persons)
whose = models.IntegerField(null=True)
views.py
def blocked(request):
blocked_list = Blocked.objects.filter(whose = user_id)
total_profiles = persons.objects.all().exclude(blocked_list)
return render_to_response('profiles/view_all.html', {'total_profiles':total_profiles,'}, context_instance=RequestContext(request),)
please correct the question if it is not correct.
You can try this:
total_profiles = persons.objects.all().exclude(id__in = blocked_list.values_list('id', flat=True))
It's untested, but adapted from this answer.
Some notes:
if persons has the default manager, you can omit all().
whose does not have an index, so it will become slow when your dataset gets big. You can use a ForeignKey field instead of an IntegerField
the common convention is to capitalize class names and to write model names in singular i.e. Person instead of persons

Django model instance from foreign key

I am reading Excel using xlrd. One of the columns has the Bank name, which is linked to vehicle model via Foreign Key. When xlrd finishes reading a row, it should save that record to vehicle table. However getting the actual pk value and error that Vehicles.bank must a Banks instance.
After checking dozens of questions related to this issue, I found this one the most similar one, but still I am not getting the expected result.
The relevant Vehicle model section is as follows:
class Vehicles(models.Model):
stock = models.CharField(max_length=10, blank=False, db_index=True)
vin = models.CharField(max_length=17, blank=False, db_index=True)
sold = models.DateField(blank=True, null=True, db_index=True)
origin = models.CharField(max_length=10, blank=False, db_index=True)
bank = models.ForeignKey('banks.Banks', db_column='bank', null=True)
I am using python 2.7, django 1.5.4 and Postgresql 9.2.5. Dbshell utility does show that banks table has a Foreign contraint referring to vehicles table, via banks(id).
Since I am not using a form for this particular part, I think it does not matter whether I use a ModelForm or not.
Current scenario: Excel file has FBANK as the cell value. There is an existing record in banks table that contains FBANK in its name column, id=2. The python line is:
def bank(value):
return Banks.objects.get(name=value).id
With the above line, error is:
Cannot assign "2": "Vehicles.bank" must be a "Banks" instance.
If I remove the ".id" at the end, error is then:
Banks matching query does not exist.
Appreciate your help.
Ricardo
When saving Vehicle you need to pass Banks instance with corresponding bank name. See example, I suppose that you have all data in corresponding cells from 0 to 4, replace with your own cells numbers:
def get_bank_instance(bank_name):
try:
bank = Banks.objects.get(name=bank_name)
except Banks.DoesNotExist:
return None
return bank
# reading excel file here, we have list of cells in a row
for cell in cells:
bank = get_bank_instance(cell[4])
if bank:
# get other cells values to be saved in Vehicles
stock, vin, sold, origin = cell[0], cell[1], cell[2], cell[3]
Vehicles.create(bank=bank, stock=stock, vin=vin, sold=sold, origin=origin)
You also can create save instance of Vehicles passing bank id directly:
b_id = Banks.objects.get(name=bank_name).id
Vehicles.create(bank_id=b_id, stock=stock, vin=vin, sold=sold, origin=origin)
Update:
create() is a built-in model method to create and save into database model instance. If you are asking about "Add a classmethod on the model class" in Django docs, this is not the case, because you are just using built-in method for the model. For some cases you can use custom method for creating new models, but I would do so if I had to pass a lot of default attributes for the new instance.
Also, it's possible to create and save new model instance by using save():
bank_instance = Banks.objects.get(name=bank_name)
vehicle = Vehicles()
vehicle.bank = bank_instance
vehicle.stock = stock
vehicle.vin = vin
vehicle.sold = sold
vehicle.origin = origin
# without save() data will not be saved to db!
vehicle.save()
It's quite long and you always need to remember to call .save(), so it's a good idea to use .create()
You should be returning a Banks instance when you want to assign it to a Vehicle model instance; so you should not have the .id part at the end of the return value for your bank() method.
Secondly, if it says that it isn't finding the Banks instance, then you should check the value of your value parameter to see what it is and try to manually do a Banks.objects.get from your database. If it can't be found then there is probably another reason for this other than using the Django ORM incorrectly.
When you are assigning instances to other instances in Django, for example setting the Bank for a Vehicle it must be an instance of the model and not the id or pk value of model; this is stated in the other StackOverflow question that you reference in your question.

Figuring out how to design my model and using "through"

I'm trying to figure out how to design my model. I've been going over the documentation, and it ultimately seems like I should be using the "through" attribute, but I just can't figure out how to get it to work how I want.
If someone could take a look and point out what I'm missing, that would be really helpful. I have pasted my model below.
This is what I am trying to do:
1) Have a list of server types
2) Each server type will need to have different parts available to that specific server type
3) The asset has a FK to the servermodel, which has a M2M to the parts specific to that server type.
My question is, how can each "Asset" store meta data for each "Part" specific to that "Asset"? For example, each "Asset" should have it's own last_used data for the part that's assigned to it.
Thanks! :)
class Part(models.Model):
part_description = models.CharField(max_length=30,unique=1)
last_used = models.CharField(max_length=30)
def __unicode__(self):
return self.part_description
class ServerModel(models.Model):
server_model = models.CharField(max_length=30,unique=1)
parts = models.ManyToManyField(Part)
def __unicode__(self):
return self.server_model
class Asset(models.Model):
server_model = models.ForeignKey(ServerModel)
serial_number = models.CharField(max_length=10,unique=1)
def __unicode__(self):
return self.server_model.server_model
EDIT:
Thank you for the help!
I may have not explained myself clearly, though. It's probably my confusing model names.
Example:
ServerModel stores the type of server being used, say "Dell Server 2000".
The "Dell Server 2000" should be assigned specific parts:
"RAM"
"HARD DISK"
"CDROM"
Then, I should be able to create 10x Assets with a FK to the ServerModel. Now, each of these assets should be able to mark when the "RAM" part was last used for this specific asset.
I'm not sure I exactly understand what you want to do, but basically you can solve that with a "through" model, as you expected:
import datetime
class Part(models.Model):
name = models.CharField(max_length=30,unique=1)
class ServerModel(models.Model):
server_model = models.CharField(max_length=30,unique=1)
parts = models.ManyToManyField(Part,through='Asset')
class Asset(models.Model):
server_model = models.ForeignKey(ServerModel)
part = models.ForeignKey(Part)
serial_number = models.CharField(max_length=10,unique=1)
used = models.DateTimeField(default=datetime.datetime.now())
First thing to notice is the relation of the parts to the servermodel using the "through"-model: that way for each Part instance assigned to the "parts"-property of a ServerModel instance a new Asset instance is created (Phew - hope that doesn't sound too complicated). At the time of creation the "used"-property of the Asset instance is set to the current date and time (thats what default=datetime.datetime.now() does).
If you do that, you can then just query the database for the last asset containing your part. That queryset can then be sorted by the "used" property of the Asset model, which is the date when the Asset instance has been created.
ServerModel.objects.filter(parts__name='ThePartYouAreLookingFor').order_by('asset__used')
I'm not absolutely sure if the queryset is correct, so if someone finds huge nonsense in it, feel free to edit ;)
edit:
The models above do not exactly that. But you do not even need a through model for what you want:
class ServerModel(models.Model):
server_model = models.CharField(max_length=30,unique=1)
parts = models.ManyToManyField(Part)
class Asset(models.Model):
server_model = models.ForeignKey(ServerModel)
parts = models.ForeignKey(Part)
serial_number = models.CharField(max_length=10,unique=1)
used = models.DateTimeField(default=datetime.datetime.now())
Basically you can just add assets and then query all assets that have a RAM in parts.
Asset.objects.filter(parts__contains='RAM').order_by('used')
Get the date of the first (or last) result of that queryset and you have the date of the last usage of your 'RAM'-part.

Fetching ManyToMany objects from multiple objects through intermediate tables

Is there an easy way to fetch the ManyToMany objects from a query that returns more than one object? The way I am doing it now doesn't feel as sexy as I would like it to. Here is how I am doing it now in my view:
contacts = Contact.objects.all()
# Use Custom Manager Method to Fetch Each Contacts Phone Numbers
contacts = PhoneNumber.objects.inject(contacts)
My Models:
class PhoneNumber(models.Model):
number = models.CharField()
type = models.CharField()
# My Custom Manager
objects = PhoneNumberManager()
class Contact(models.Model):
name = models.CharField()
numbers = models.ManyToManyField(PhoneNumber, through='ContactPhoneNumbers')
class ContactPhoneNumbers(models.Model):
number = models.ForeignKey(PhoneNumber)
contact = models.ForeignKey(Contact)
ext = models.CharField()
My Custom Manager:
class PhoneNumberManager(models.Manager):
def inject(self, contacts):
contact_ids = ','.join([str(item.id) for item in contacts])
cursor = connection.cursor()
cursor.execute("""
SELECT l.contact_id, l.ext, p.number, p.type
FROM svcontact_contactphonenumbers l, svcontact_phonenumber p
WHERE p.id = l.number_id AND l.contact_id IN(%s)
""" % contact_ids)
result = {}
for row in cursor.fetchall():
id = str(row[0])
if not id in result:
result[id] = []
result[id].append({
'ext': row[1],
'number': row[2],
'type': row[3]
})
for contact in contacts:
id = str(contact.id)
if id in result:
contact.phonenumbers = result[id]
return contacts
There are a couple things you can do to find sexiness here :-)
Django does not have any OOTB way to inject the properties of the through table into your Contact instance. A M2M table with extra data is a SQL concept, so Django wouldn't try to fight the relations, nor guess what should happen in the event of namespace collision, etc... . In fact, I'd go so far as to say that you probably do not want to inject arbitrary model properties onto your Contact object... if you find yourself needing to do that, then it's probably a sign you should revise your model definition.
Instead, Django provides convenient ways to access the relation seamlessly, both in queries and for data retrieval, all the while preserving the integrity of the entities. In this case, you'll find that your Contact object offers a contactphonenumbers_set property that you can use to access the through data:
>>> c = Contact.objects.get(id=1)
>>> c.contactphonenumbers_set.all()
# Would produce a list of ContactPhoneNumbers objects for that contact
This means, in your case, to iterate of all contact phone numbers (for example) you would:
for contact in Contact.objects.all():
for phone in contact.contactphonenumbers_set.all():
print phone.number.number, phone.number.type, phone.ext
If you really, really, really want to do the injection for some reason, you'll see you can do that using the 3-line code sample immediately above: just change the print statements into assignment statements.
On a separate note, just for future reference, you could have written your inject function without SQL statements. In Django, the through table is itself a model, so you can query it directly:
def inject(self, contacts):
contact_phone_numbers = ContactPhoneNumbers.objects.\
filter(contact__in=contacts)
# And then do the result construction...
# - use contact_phone_number.number.phone to get the phone and ext
# - use contact_phone_number.contact to get the contact instance