I am trying to retrieve and print an item in a one to many relationship (in this case I want to print an Article with all it's comments), in elixir/ecto.
I am getting the following error -
[error] GenServer #PID<0.398.0> terminating
** (Ecto.QueryError) deps/ecto/lib/ecto/association.ex:516: field `articles_id` in `where` does not exist in schema Newsly.Comments in query:
from c in Newsly.Comments,
where: c.articles_id == ^1,
order_by: [asc: c.articles_id],
select: {c.articles_id, c}
(elixir) lib/enum.ex:1826: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir) lib/enum.ex:1372: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(elixir) lib/enum.ex:1826: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/repo/queryable.ex:124: Ecto.Repo.Queryable.execute/5
(ecto) lib/ecto/repo/queryable.ex:37: Ecto.Repo.Queryable.all/4
(elixir) lib/enum.ex:1270: Enum."-map/2-lists^map/1-0-"/2
Last message: %Phoenix.Socket.Message{event: "addComment", join_ref: "43", payload: %{"articleid" => 1, "message" => "asdfasdfsasdfasdferqweasdfas", "user" => "peter"}, ref: "45", topic: "room:lobby"}
State: %Phoenix.Socket{assigns: %{}, channel: Newsly.RoomChannel, channel_pid: #PID<0.398.0>, endpoint: Newsly.Endpoint, handler: Newsly.UserSocket, id: nil, join_ref: "43", joined: true, private: %{log_handle_in: :debug, log_join: :info}, pubsub_server: Newsly.PubSub, ref: nil, serializer: Phoenix.Transports.V2.WebSocketSerializer, topic: "room:lobby", transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.389.0>, vsn: "2.0.0"}
What is strange about this error is that it says that it cannot find articles_id, but I am calling article_id when I make the preload of the comments, and throughout the rest of my code.
Here is where I call the Repo
defmodule Newsly.CommentController do
alias Newsly.{Repo, Articles, Comments}
def addComment(articleid, message, user) do
IO.puts "inside addComment() in CommentController"
article = Repo.get(Articles, articleid)
|> Repo.preload(:comments) #this line is giving the error - when I take it out the error goes away (although comments are not preloaded)
IO.puts "article"
IO.inspect article
end
end
Here is my model -
defmodule Newsly.Comments do
use Newsly.Web, :model
schema "comment" do
field :body, :string
field :user, :string
field :upvotes, :integer
field :downvotes, :integer
field :flaggedcount, :integer
belongs_to :article, Newsly.Articles, foreign_key: :article_id #I don't call articles_id here!!! only article_id (singular). Ecto is seeing in the database a relationship that does not exist!
timestamps()
end
#doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:body, :user, :upvotes, :downvotes, :flaggedcount])
|> validate_required([:body, :user, :upvotes, :downvotes, :flaggedcount])
end
end
And just to doublecheck....
Here is the postgres database association I have locally - notice that article_id exists (not articles_id)
newsly_dev=# table comment;
id | body | user | upvotes | downvotes | flaggedcount | article_id | inserted_at | updated_at
----+------+------+---------+-----------+--------------+------------+-------------+------------
(0 rows)
I'm completely lost. Anyone have any ideas?
Since you're first fetching an article and then fetching its comments, Ecto will use the has_many declaration of Articles model and you're most likely missing the custom foreign_key in that.
has_many :comments, Newsly.Comments, foreign_key: :article_id
^^^^^^^^^^^^^^^^^^^^^^^^
Related
I tried to write a wrapper for ets with which you can read and write structures in etc, question is: how to make id to be generated automatically
defmodule StructTable do
defstruct id: 0, data: nil
def create_table do
:ets.new(__MODULE__, [:orderedset, :named_table, {:keypos, 1}])
end
def insert_into_table(%__MODULE__{ id: id, data: data}) do
if hd(:ets.lookup(__MODULE__, id)) == false do
:ets.insert(__MODULE__, {id,data})
else IO.puts("already exists")
end
end
def select_data(iid) do
hd(:ets.lookup(__MODULE__, iid))
end
def select_all do
:ets.tab2list(__MODULE__)
end
end
I used :ets.last() to get the last key and added one to it; if it was an empty table, it gets an id of 1. Table type is ordered_set, which sorts keys.
defmodule StructTable do
defstruct id: 0, data: nil
def create_table do
:ets.new(__MODULE__, [:ordered_set, :public, :named_table])
end
def insert_into_table(%__MODULE__{data: data}) do
:ets.insert(__MODULE__, {generate_id(), data})
end
def select_all do
:ets.tab2list(__MODULE__)
end
def generate_id() do
if :ets.tab2list(__MODULE__) == [] do
1
else
(:ets.last(__MODULE__) ) + 1
end
end
def select_data(iid) do
hd(:ets.lookup(__MODULE__, iid))
end
end
Using Powershell I am calling win32_computersystem. I want to list data about the machine including $_thermalstate - Here is my code
The code looks as though it should work but returns a empty value. I want to create an inline array or hash table that the value $_.thermalstate references.
Get-WmiObject win32_computersystem | select Name, Model, Caption, #{n="Timezone"; e={$_.currenttimezone}}, Description, DNShostname,Domain,#{n='Domain Role'; E={$_.domainrole}},Roles,Status,#{n='System Type'; e={$_.systemtype}},#{n='Thermal State'; e={$_.thermalstate[#{'3'='safe'}]}}
Output
Name : MYPC
Model : Latitude E5470
Caption : MYPC
Timezone : 600
Description : AT/AT COMPATIBLE
DNShostname : MYPC
Domain : work.biz
Domain Role : 1
Roles : {LM_Workstation, LM_Server, NT}
Status : OK
System Type : x64-based PC
Thermal State : Safe
your lookup structure was ... wrong. [grin]
replace the last line of this reformatted version of your code ...
Get-WmiObject win32_computersystem |
Select-Object Name, Model, Caption,
#{n="Timezone"; e={$_.currenttimezone}},
Description, DNShostname,Domain,
#{n='Domain Role'; E={$_.domainrole}},
Roles,Status,
#{n='System Type'; e={$_.systemtype}},
#{n='Thermal State'; e={$_.thermalstate[#{'3'='safe'}]}}
... with this line ...
#{n='Thermal State'; e={#{'3'='Safe'}["$($_.ThermalState)"]}}
note that the lookup table is on the OUTSIDE of the [] and that the value is forced to a string.
however, i would NOT do it this way. it's too finicky. create the lookup table BEFORE your call and use that to perform the lookup.
Your code looks like you are trying to declare/initialize a hash table while also trying to use thermalstate as a hash array.
If you initialize the hash array first, the code looks like this:
$h = #{'3'='safe'}; Get-WmiObject win32_computersystem | select Name, Model, Caption, #{n="Timezone"; e={$_.currenttimezone}}, Description, DNShostname,Domain,#{n='Domain Role';E={$_.domainrole}},Roles,Status,#{n='System Type'; e={$_.systemtype}},#{n='Thermal State'; e={$h[$_.thermalstate.toString()]}}
According to https://wutils.com/wmi/root/cimv2/win32_computersystem/
ThermalState property
CIMTYPE 'uint16'
Description 'The ThermalState property identifies the enclosure's thermal state when last booted.'
MappingStrings ['SMBIOS|Type 3|System Enclosure or Chassis|Thermal State']
read True
ValueMap ['1', '2', '3', '4', '5', '6']
Values ['Other', 'Unknown', 'Safe', 'Warning', 'Critical', 'Non-recoverable']
ThermalState property is in 1 class (Win32_ComputerSystem) of ROOT\cimv2 and in 2 namespaces
You could create an enum
enum ThermalState {
Other = 1
Unknown = 2
Safe = 3
Warning = 4
Critical = 5
NonRecoverable = 6
}
And use that to get a verbose response from the property
Get-WmiObject win32_computersystem | Select-Object Name, Model, Caption,
#{n="Timezone"; e={$_.currenttimezone}}, Description, DNShostname,Domain,
#{n='Domain Role';E={$_.domainrole}},Roles,Status,
#{n='System Type'; e={$_.systemtype}},
#{n='Thermal State'; e={[ThermalState]$_.thermalstate}}
Sample output
Name : HP-G1610
Model : ProLiant MicroServer Gen8
Caption : HP-G1610
Timezone : 120
Description : AT/AT COMPATIBLE
DNShostname : HP-G1610
Domain : DOMAIN
Domain Role : 0
Roles : {...}
Status : OK
System Type : x64-based PC
Thermal State : Safe
In general to get a list of an enum :
> $Enum ='System.DayOfWeek'
> [Enum]::GetValues($Enum) | ForEach-Object {'{0} {1}' -f [int]$_,$_ }
0 Sunday
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
I have created a basic setup for autocomplete-light on one of my forms. I am using direct SQL queries to access the data I require, and the Select2ListView to send the data to the template. The autocomplete data is available on the browser, but it is case sensitive. Can someone please suggest what I can do to make the autocomplete-light become case insensitive?
I read about using 'split_words = True' from the AutocompleteModel class, but I have no idea how to do this.
For example (q = 'ger'):
Command Prompt (views.py print statement) returns
['Algeria', 'Germany', 'Niger (the), 'Nigeria']
http://127.0.0.1:8000/autocomplete/country-autocomplete?q=ger returns
{"results": [{"text": "Algeria", "id": "Algeria"}, {"text": "Niger (the)", "id": "Niger (the)"}, {"text": "Nigeria", "id": "Nigeria"}]}
As you can see, Germany is visible in the data I am returning from the view, but there is some additional filtering going on by autocomplete-light. I need to change autocomplete-light so that the filtering it performs is case-insensitive. Any ideas?
I am running Python 3.5.2, and the latest pip install for autocomplete-light.
Thanks
Stephen
View
class CountryAutocomplete(autocomplete.Select2ListView):
def get_list(self):
# Only allow authenticated users
if not self.request.user.is_authenticated():
return []
# Read all country names from database
cursor = connection.cursor()
cursor.execute('SELECT country_name FROM autocomplete_iso3166;')
results = cursor.fetchall()
# Flatten database query into a list
results = [e for l in results for e in l]
# Check POST query and filter further
if self.q:
# Make query case insensitive
results = list(filter(lambda k: self.q.casefold() in str(k).casefold(), results))
print(results)
return results
Form
class VendorForm(forms.ModelForm):
class Meta:
model = client
fields = [
'vendor_name', 'address5', 'address4', 'address3', 'address2', 'address1',
]
widgets = {
'address5': autocomplete.ListSelect2(url='country-autocomplete'),
'address4': autocomplete.ListSelect2(url='state-autocomplete',forward=['address5'])
}
Model
class client(models.Model):
vendor_name = models.CharField(max_length=100)
user_admin = models.ForeignKey(User, unique=False, null=True, blank=True)
address1 = models.CharField(null=True, blank=True, max_length=200)
address2 = models.CharField(null=True, blank=True, max_length=50)
address3 = models.CharField(null=True, blank=True, max_length=50)
address4 = models.CharField(null=True, blank=True, max_length=50)
address5 = models.CharField(null=True, blank=True, max_length=50)
if self.q:
queryset = queryset.filter(country_name__istartswith=self.q)
i means case insensitive. This example uses the beginning of the field.
If you want an exact match case-insensitive it would be:
country_name__iexact=self.q
I want to create records on two different tables (venue and parking) via one form using accepts_nested_attributes_for. I want a user to be able to create a new venue, and also specify the parking options available to that venue via checkboxes. When I submit the form, the record for the containing model (venue) is created, but nothing happens with the nested model (parking). When I check the response from the server, I see that I'm encountering "Unpermitted parameters: parking_attributes," although I'm not sure why.
I've watched Railscast #196 Nested Model Form, and tried the suggestions from multiple stackoverflow posts (Rails 4 nested attributes not saving, Rails 4: fields_for in fields_for, and Rails 4 - Nested models(2 levels) not saving). If anybody can help me out, I'd greatly appreciate it.
I've included the two models, the venues controller, the venues/new view, and the response from the server.
venue.rb
class Venue < ActiveRecord::Base
has_many :parkings
accepts_nested_attributes_for :parkings
end
parking.rb
class Parking < ActiveRecord::Base
belongs_to :venue
end
venues_controller.rb
class VenuesController < ApplicationController
def index
#venues = Venue.all
end
def new
#venue = Venue.new
end
def create
#venue = Venue.new(venue_params)
if #venue.save
redirect_to #venue, flash: { success: "Venue successfully created" }
else
render :new
end
end
def show
#venue = Venue.find(params[:id])
end
def edit
#venue = Venue.find(params[:id])
end
def update
#venue = Venue.find(params[:id])
if #venue.update(venue_params)
redirect_to #venue
else
render "edit"
end
end
def destroy
#venue = Venue.find(params[:id])
if #venue.destroy
redirect_to venues_path, flash: { success: "Venue successfully destroyed" }
else
render "show", flash: { error: "Venue was not successfully destroyed" }
end
end
private
def venue_params
params.require(:venue).permit(
:name,:address,:city,:state,:zip,
parking_attributes: [:id, :venue_id, :none, :street_free])
end
end
/venues/new.haml
%h1 Add a new venue
= form_for #venue do |f|
= f.label :name
= f.text_field :name
= f.label :address
= f.text_field :address
= f.label :city
= f.text_field :city
= f.label :state
= f.text_field :state
= f.label :zip
= f.text_field :zip
= f.fields_for :parkings do |p|
= p.label :none
= p.check_box :none
= p.label :street_free
= p.check_box :street_free
= f.submit
Server response
Started POST "/venues" for 127.0.0.1 at 2014-04-29 14:02:54 -0500
Processing by VenuesController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"kMcVVwXq7f22rIGm1rQ6+QzC80ScmXrVA2IE8TGbN7w=",
"venue"=>{"name"=>"The Five O'Clock Lounge",
"address"=>"11904 Detroit Ave",
"city"=>"Lakewood",
"state"=>"OH",
"zip"=>"44107",
"parkings_attributes"=>
{"0"=>
{"none"=>"1",
"street_free"=>"0"
}
}
},
"commit"=>"Create Venue"}
Unpermitted parameters: parkings_attributes
(0.2ms) BEGIN
SQL (107.0ms) INSERT INTO "venues" (
"address",
"city",
"created_at",
"name", "state",
"updated_at", "zip"
) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"
[
["address", "11904 Detroit Ave"],
["city", "Lakewood"],
["created_at", Tue, 29 Apr 2014 19:02:54 UTC +00:00],
["name", "The Five O'Clock Lounge"],
["state", "OH"],
["updated_at", Tue, 29 Apr 2014 19:02:54 UTC +00:00],
["zip", 44107]
]
SQL (47.5ms) INSERT INTO "parkings" (
"created_at",
"updated_at",
"venue_id") VALUES ($1, $2, $3) RETURNING "id"
[
["created_at", Tue, 29 Apr 2014 19:02:54 UTC +00:00],
["updated_at", Tue, 29 Apr 2014 19:02:54 UTC +00:00],
["venue_id", 10]
]
(0.6ms) COMMIT
Redirected to http://localhost:3000/venues/10
Completed 302 Found in 165ms (ActiveRecord: 155.2ms)
UPDATE: SOLVED
Following the advice of Kirti, I was able to get past the unpermitted parameters error.
Update venue_params method as below:
def venue_params
params.require(:venue).permit(
:name,:address,:city,:state,:zip,
parkings_attributes: [:id, :venue_id, :none, :street_free])
end
Notice parkings_attributes(plural parkings) and not parking_attributes(singular parking).
As you have 1-M relationship between Venue and Parking model you would receive parkings_attributes(plural parkings) in params hash BUT in your current code for venue_params you whitelisted parking_attributes(singular parking). This is causing the warning Unpermitted parameters: parkings_attributes
I have an Event model that I'd like to place the following validation rule on, in a custom def clean(self): method on the Model:
def clean(self):
from django.core.exceptions import ValidationError
if self.end_date is not None and self.start_date is not None:
if self.end_date < self.start_date:
raise ValidationError('Event end date should not occur before start date.')
Which works fine, except that I'd like to highlight the self.end_date field in the admin UI, by somehow nominating it as the field that has errors. Otherwise I only get the error message that occurs at the top of the change form.
From Django 1.7 you can directly add error to the particular field by using add_error method. Django docs
form.add_error('field_name', 'error_msg or ValidationError instance')
If the field_name is None the error will be added to non_field_errors.
def clean(self):
cleaned_data = self.cleaned_data
end_date = cleaned_data.get('end_date')
start_date = cleaned_data.get('start_date')
if end_date and start_date:
if end_date < start_date:
self.add_error('end_date', 'Event end date should not occur before start date.')
# You can use ValidationError as well
# self.add_error('end_date', form.ValidationError('Event end date should not occur before start date.'))
return cleaned_data
The docs explain how to do this at the bottom.
provided example:
class ContactForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = self.cleaned_data
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject and "help" not in subject:
# We know these are not in self._errors now (see discussion
# below).
msg = u"Must put 'help' in subject when cc'ing yourself."
self._errors["cc_myself"] = self.error_class([msg])
self._errors["subject"] = self.error_class([msg])
# These fields are no longer valid. Remove them from the
# cleaned data.
del cleaned_data["cc_myself"]
del cleaned_data["subject"]
# Always return the full collection of cleaned data.
return cleaned_data
for your code:
class ModelForm(forms.ModelForm):
# ...
def clean(self):
cleaned_data = self.cleaned_data
end_date = cleaned_data.get('end_date')
start_date = cleaned_data.get('start_date')
if end_date and start_date:
if end_date < start_date:
msg = 'Event end date should not occur before start date.'
self._errors['end_date'] = self.error_class([msg])
del cleaned_data['end_date']
return cleaned_data